Skip to content

Commit

Permalink
refactor: add some utils for stabilize flaky (#6869)
Browse files Browse the repository at this point in the history
Co-authored-by: taiga-family-bot <[email protected]>
  • Loading branch information
splincode and taiga-family-bot authored Feb 29, 2024
1 parent 9a7dff9 commit f49763c
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 48 deletions.
60 changes: 30 additions & 30 deletions projects/demo-playwright/tests/core/dropdown/dropdown.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,56 @@ test.describe('Dropdown', () => {
test.use({viewport: {width: 720, height: 720}});

test('base', async ({page}) => {
await tuiGoto(page, '/directives/dropdown#base');
await new TuiDocumentationPagePO(page)
.getExample('#base')
.locator('button')
.click();
await tuiGoto(page, '/directives/dropdown');
const example = new TuiDocumentationPagePO(page).getExample('#base');

await example.scrollIntoViewIfNeeded();
await example.locator('button').click();
await expect(page).toHaveScreenshot('01-dropdown.png');
});

test('Interesting', async ({page}) => {
await tuiGoto(page, '/directives/dropdown#full-featured');
await new TuiDocumentationPagePO(page)
.getExample('#full-featured')
.locator('input')
.click();
await tuiGoto(page, '/directives/dropdown');
const example = new TuiDocumentationPagePO(page).getExample('#full-featured');

await example.scrollIntoViewIfNeeded();
await example.locator('input').click();
await expect(page).toHaveScreenshot('02-dropdown.png');
});

test('Appearance', async ({page}) => {
await tuiGoto(page, '/directives/dropdown#appearance');
await new TuiDocumentationPagePO(page)
.getExample('#appearance')
.locator('input')
.click();
await tuiGoto(page, '/directives/dropdown');
const example = new TuiDocumentationPagePO(page).getExample('#appearance');

await example.scrollIntoViewIfNeeded();
await example.locator('input').click();
await expect(page).toHaveScreenshot('03-dropdown.png');
});

test('Hosted dropdown', async ({page}) => {
await tuiGoto(page, '/components/hosted-dropdown#menu');
await new TuiDocumentationPagePO(page)
.getExample('#menu')
.locator('button')
.click();
await tuiGoto(page, '/components/hosted-dropdown');
const example = new TuiDocumentationPagePO(page).getExample('#menu');

await example.scrollIntoViewIfNeeded();
await example.locator('button').click();
await expect(page).toHaveScreenshot('04-dropdown.png');
});

test('Hosted dropdown and custom position', async ({page}) => {
await tuiGoto(page, '/components/hosted-dropdown#position');
await new TuiDocumentationPagePO(page)
.getExample('#position')
.locator('button')
.click();
await tuiGoto(page, '/components/hosted-dropdown');
const example = new TuiDocumentationPagePO(page).getExample('#position');

await example.scrollIntoViewIfNeeded();
await example.locator('button').click();
await expect(page).toHaveScreenshot('05-dropdown.png');
});

test('Esc -> Hosted Dropdown', async ({page}) => {
await tuiGoto(page, '/components/hosted-dropdown#tui-dropdown-host');
await new TuiDocumentationPagePO(page)
.getExample('#tui-dropdown-host')
.locator('button[tuiHostedDropdownHost]')
.click();
await tuiGoto(page, '/components/hosted-dropdown');
const example = new TuiDocumentationPagePO(page).getExample('#tui-dropdown-host');

await example.scrollIntoViewIfNeeded();
await example.locator('button[tuiHostedDropdownHost]').click();
await expect(page).toHaveScreenshot('06-dropdown.png');

await page
Expand Down
6 changes: 3 additions & 3 deletions projects/demo-playwright/tests/deep/deep-select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ test.describe('Deep / Select', () => {
}

await api.focusOnBody();
await api.networkidle();
await select.scrollIntoViewIfNeeded();
await expect(select).toBeVisible();
await select.click();
Expand All @@ -32,9 +33,9 @@ test.describe('Deep / Select', () => {
for (const [index, option] of options.entries()) {
await option.focus();
await page.keyboard.down('Enter');
await api.networkidle();
await api.focusOnBody();
await api.hideNotifications();
await api.networkidle();

await expect(api.apiPageExample).toHaveScreenshot(
`deep-${path}__${name}-select-option-${index}.png`,
Expand All @@ -47,13 +48,12 @@ test.describe('Deep / Select', () => {

if (cleaner) {
await cleaner.click();
await api.networkidle();
} else {
await options[0].focus();
await page.keyboard.down('Enter');
await api.networkidle();
}

await api.networkidle();
await api.focusOnBody();
}
}),
Expand Down
5 changes: 4 additions & 1 deletion projects/demo-playwright/tests/demo/landing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ test.describe('Landing', () => {
});

test('take snapshot', async ({page}) => {
await tuiGoto(page, '/', {hideHeader: false});
await tuiGoto(page, '/', {
hideHeader: false,
});

await expect(page).toHaveScreenshot('landing-360-740.png');
});
});
13 changes: 8 additions & 5 deletions projects/demo-playwright/utils/goto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {expect, Page} from '@playwright/test';

import {tuiRemoveElement} from './hide-element';
import {tuiMockDate} from './mock-date';
import {tuiWaitForFonts} from './wait-for-fonts';
import {waitStableState} from './wait-stable-state';

interface TuiGotoOptions extends NonNullable<Parameters<Page['goto']>[1]> {
date?: Date;
Expand Down Expand Up @@ -48,23 +50,24 @@ export async function tuiGoto(
await tuiMockDate(page, date);

const response = await page.goto(url, playwrightGotoOptions);
const app = page.locator('app');

await page.waitForLoadState('domcontentloaded');
await page.waitForLoadState('load');

await expect(page.locator('app')).toHaveClass(/_loaded/, {timeout: 30_000});
await expect(app).toHaveClass(/_loaded/, {timeout: 30_000});
await tuiWaitForFonts(page);
await waitStableState(app);

if (hideHeader) {
await page.locator('[tuidocheader]').evaluate(el => el.remove());
await tuiRemoveElement(page.locator('[tuiDocHeader]'));
}

if (hideVersionManager) {
await page.locator('version-manager').evaluate(el => el.remove());
await tuiRemoveElement(page.locator('version-manager'));
}

if (hideLanguageSwitcher) {
await page.locator('tui-language-switcher').evaluate(el => el.remove());
await tuiRemoveElement(page.locator('tui-language-switcher'));
}

expect(
Expand Down
4 changes: 4 additions & 0 deletions projects/demo-playwright/utils/hide-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ export async function tuiHideElement(element: Locator): Promise<void> {
el.style.opacity = '0';
});
}

export async function tuiRemoveElement(element: Locator): Promise<void> {
return element.evaluate(el => el.remove());
}
1 change: 1 addition & 0 deletions projects/demo-playwright/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './mock-date';
export * from './mock-images';
export * from './page-objects';
export * from './wait-for-fonts';
export * from './wait-stable-state';
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import {expect, Locator, Page, Request} from '@playwright/test';

import {tuiHideElement} from '../hide-element';
import {waitStableState} from '../wait-stable-state';

export class TuiDocumentationApiPagePO {
private readonly pending = new Set<Request>();

public readonly pageExamples: Locator = this.page.locator('tui-doc-example');
public readonly apiPageExample: Locator = this.page.locator('#demo-content');

constructor(protected readonly page: Page) {
page.on('request', request => this.pending.add(request));
page.on('requestfailed', request =>
setTimeout(() => this.pending.delete(request)),
);
page.on('requestfinished', request =>
setTimeout(() => this.pending.delete(request)),
);
page.on('requestfailed', request => this.pending.delete(request));
page.on('requestfinished', request => this.pending.delete(request));
}

/**
* await page.waitForLoadState('networkidle');
* Doesn't work as expected
*/
public async networkidle(): Promise<void> {
await this.page.waitForTimeout(100);

await Promise.all(
[...this.pending].map(
async req =>
Expand All @@ -35,7 +31,13 @@ export class TuiDocumentationApiPagePO {
),
);

await this.page.waitForTimeout(200);
if ((await this.apiPageExample.all()).length) {
await waitStableState(this.apiPageExample);
} else if ((await this.pageExamples.all()).length) {
for (const example of await this.pageExamples.all()) {
await waitStableState(example);
}
}
}

public async hideNotifications(): Promise<void> {
Expand Down
17 changes: 17 additions & 0 deletions projects/demo-playwright/utils/wait-stable-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Locator} from '@playwright/test';

export async function waitStableState(locator: Locator): Promise<void> {
try {
const handle = await locator.elementHandle();

// https://playwright.dev/docs/actionability#stable
// element is Stable, as in not animating or completed animation
// Element is considered stable when it has maintained
// the same bounding box for at least two consecutive animation frames.
await handle?.waitForElementState('stable');

// https://playwright.dev/docs/actionability#visible
// Element is considered visible when it has non-empty bounding box
await handle?.waitForElementState('visible');
} catch {}
}

0 comments on commit f49763c

Please sign in to comment.