Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 73 additions & 3 deletions core/src/components/title/test/basic/title.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,83 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

configs({ modes: ['ionic-md', 'md', 'ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
configs({ modes: ['ionic-md', 'md', 'ios'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('title: basic'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto('/src/components/title/test/basic', config);
await page.setContent(
`
<style>
.ios .hide-ios,
.ionic .hide-ios {
display: none;
}
</style>

<div id="header-wrapper">
<ion-header>
<ion-toolbar>
<ion-title>Settings</ion-title>
</ion-toolbar>
</ion-header>

<ion-header>
<ion-toolbar>
<ion-title>Browse</ion-title>
<ion-buttons slot="end">
<ion-button>Cancel</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>

<ion-header>
<ion-toolbar class="toolbar-title-default">
<ion-buttons slot="start">
<ion-back-button default-href="#"></ion-back-button>
</ion-buttons>
<ion-title>Title</ion-title>
<ion-title class="hide-ios" size="small">Subtitle</ion-title>
<ion-buttons slot="end">
<ion-button><ion-icon slot="icon-only" name="search"></ion-icon></ion-button>
<ion-button><ion-icon slot="icon-only" name="menu"></ion-icon></ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>

<ion-header>
<ion-toolbar>
<ion-title size="small">Choose a contact to add to Favorites</ion-title>
</ion-toolbar>
<ion-toolbar>
<ion-title>Contacts</ion-title>
<ion-buttons slot="end">
<ion-button>Cancel</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>

<ion-header>
<ion-toolbar color="secondary">
<ion-title size="small">
Choose multiple contacts to add to your Favorites so this just goes on and on and on
</ion-title>
</ion-toolbar>
<ion-toolbar color="secondary">
<ion-buttons slot="start">
<ion-button>Groups</ion-button>
</ion-buttons>
<ion-title>Contacts</ion-title>
<ion-buttons slot="end">
<ion-button>Cancel</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
</div>
`,
config
);

const wrapper = page.locator('#header-wrapper');

// only screenshot the headers to avoid unnecessary blank space from ion-content
await expect(wrapper).toHaveScreenshot(screenshot(`title-basic`));
});
});
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions core/src/components/title/test/color/title.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

/**
* This behavior does not vary across directions.
*/
configs({ modes: ['ionic-md', 'md', 'ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
test.describe(title('title: color'), () => {
test('should not have visual regressions', async ({ page }) => {
await page.setContent(
`
<ion-header>
<ion-toolbar>
<ion-title color="primary">Primary</ion-title>
</ion-toolbar>
</ion-header>
`,
config
);

const header = page.locator('ion-header');

await expect(header).toHaveScreenshot(screenshot(`title-color`));
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 89 additions & 0 deletions core/src/components/title/test/title.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { newSpecPage } from '@stencil/core/testing';

import { ToolbarTitle } from '../title';

describe('title: classes', () => {
it('should add the default size class when size is not set', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: `<ion-title>Title</ion-title>`,
});

expect(page.root!.classList.contains('title-default')).toBe(true);
});

it('should add the size class when size is set', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: `<ion-title size="large">Title</ion-title><ion-title size="small">Title</ion-title>`,
});

const [large, small] = Array.from(page.body.querySelectorAll('ion-title'));
expect(large.classList.contains('title-large')).toBe(true);
expect(small.classList.contains('title-small')).toBe(true);
});

it('should add the color classes when color is set', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: `<ion-title color="primary">Title</ion-title>`,
});

expect(page.root!.classList.contains('ion-color')).toBe(true);
expect(page.root!.classList.contains('ion-color-primary')).toBe(true);
});

it('should add the rtl class when the document is rtl', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: `<ion-title>Title</ion-title>`,
direction: 'rtl',
});

expect(page.root!.classList.contains('title-rtl')).toBe(true);
});

/**
* The iOS collapsing header and page transition look up this
* element and measure it, so it must keep its class.
*/
it('should render the inner title element', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: `<ion-title>Title</ion-title>`,
});

expect(page.root!.shadowRoot!.querySelector('.toolbar-title')).not.toBeNull();
});
});

describe('title: style event', () => {

@brandyscarney brandyscarney Oct 16, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we also cover this event being emit on the first connect?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b365e48.

it('should emit the size class when it first connects', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: '',
});

const ionStyle = jest.fn();
page.body.addEventListener('ionStyle', (ev: Event) => ionStyle((ev as CustomEvent).detail));

await page.setContent(`<ion-title size="large">Title</ion-title>`);

expect(ionStyle).toHaveBeenCalledWith({ 'title-large': true });
});

it('should emit the size class when size changes', async () => {
const page = await newSpecPage({
components: [ToolbarTitle],
html: `<ion-title>Title</ion-title>`,
});

const ionStyle = jest.fn();
page.root!.addEventListener('ionStyle', (ev: Event) => ionStyle((ev as CustomEvent).detail));

page.root!.setAttribute('size', 'large');
await page.waitForChanges();

expect(ionStyle).toHaveBeenCalledWith({ 'title-large': true });
});
});
Loading