Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ log.txt
.idea/
.versions/
.vscode/
components/
/components/
node_modules/
tmp/
dist/
Expand All @@ -23,3 +23,4 @@ www/
/test-results/
/playwright-report/
/playwright/.cache/
/src/**/*-snapshots
10 changes: 10 additions & 0 deletions src/components/icon/test/icon.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';

test.describe('icon: basic', () => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/`);

expect(await page.screenshot({ fullPage: true })).toMatchSnapshot(`icon-diff.png`);
});
});
2 changes: 2 additions & 0 deletions src/utils/test/playwright/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './playwright-page';
export * from './playwright-declarations';
18 changes: 18 additions & 0 deletions src/utils/test/playwright/page/utils/goto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Page, TestInfo } from '@playwright/test';

/**
* This is an extended version of Playwright's
* page.goto method. In addition to performing
* the normal page.goto work, this code also
* automatically waits for the Stencil components
* to be hydrated before proceeding with the test.
*/
export const goto = async (page: Page, url: string, options: any, _: TestInfo, originalFn: typeof page.goto) => {

const result = await Promise.all([
page.waitForFunction(() => (window as any).testAppLoaded === true, { timeout: 4750 }),
originalFn(url, options),
]);

return result[1];
};
1 change: 1 addition & 0 deletions src/utils/test/playwright/page/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './goto';
57 changes: 57 additions & 0 deletions src/utils/test/playwright/playwright-declarations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { Page, Response } from '@playwright/test';

export interface E2EPage extends Page {
/**
* Returns the main resource response. In case of multiple redirects, the navigation will resolve with the response of the
* last redirect.
*
* The method will throw an error if:
* - there's an SSL error (e.g. in case of self-signed certificates).
* - target URL is invalid.
* - the `timeout` is exceeded during navigation.
* - the remote server does not respond or is unreachable.
* - the main resource failed to load.
*
* The method will not throw an error when any valid HTTP status code is returned by the remote server, including 404 "Not
* Found" and 500 "Internal Server Error". The status code for such responses can be retrieved by calling
* [response.status()](https://playwright.dev/docs/api/class-response#response-status).
*
* > NOTE: The method either throws an error or returns a main resource response. The only exceptions are navigation to
* `about:blank` or navigation to the same URL with a different hash, which would succeed and return `null`.
* > NOTE: Headless mode doesn't support navigation to a PDF document. See the
* [upstream issue](https://bugs.chromium.org/p/chromium/issues/detail?id=761295).
*
* Shortcut for main frame's [frame.goto(url[, options])](https://playwright.dev/docs/api/class-frame#frame-goto)
* @param url URL to navigate page to. The url should include scheme, e.g. `https://`. When a `baseURL` via the context options was provided and the passed URL is a path, it gets merged via the
* [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor.
*/
goto: (
url: string,
options?: {
/**
* Referer header value. If provided it will take preference over the referer header value set by
* [page.setExtraHTTPHeaders(headers)](https://playwright.dev/docs/api/class-page#page-set-extra-http-headers).
*/
referer?: string;

/**
* Maximum operation time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be
* changed by using the
* [browserContext.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-navigation-timeout),
* [browserContext.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-browsercontext#browser-context-set-default-timeout),
* [page.setDefaultNavigationTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-navigation-timeout)
* or [page.setDefaultTimeout(timeout)](https://playwright.dev/docs/api/class-page#page-set-default-timeout) methods.
*/
timeout?: number;

/**
* When to consider operation succeeded, defaults to `load`. Events can be either:
* - `'domcontentloaded'` - consider operation to be finished when the `DOMContentLoaded` event is fired.
* - `'load'` - consider operation to be finished when the `load` event is fired.
* - `'networkidle'` - consider operation to be finished when there are no network connections for at least `500` ms.
* - `'commit'` - consider operation to be finished when network response is received and the document started loading.
*/
waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit';
}
) => Promise<null | Response>;
}
57 changes: 57 additions & 0 deletions src/utils/test/playwright/playwright-page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type {
PlaywrightTestArgs,
PlaywrightTestOptions,
PlaywrightWorkerArgs,
PlaywrightWorkerOptions,
TestInfo,
} from '@playwright/test';
import { test as base } from '@playwright/test';

import {
goto as goToPage,
} from './page/utils';
import type { E2EPage } from './playwright-declarations';

type CustomTestArgs = PlaywrightTestArgs &
PlaywrightTestOptions &
PlaywrightWorkerArgs &
PlaywrightWorkerOptions & {
page: E2EPage;
};

type CustomFixtures = {
page: E2EPage;
};

/**
* Extends the base `page` test figure within Playwright.
* @param page The page to extend.
* @param testInfo The test info.
* @returns The modified playwright page with extended functionality.
*/
export async function extendPageFixture(page: E2EPage, testInfo: TestInfo) {
const originalGoto = page.goto.bind(page);

/**
* Adds a global flag on the window that the test suite
* can use to determine when it is safe to execute tests
* on hydrated Stencil components.
*/
await page.addInitScript(`
(function() {
window.addEventListener('appload', () => {
window.testAppLoaded = true;
});
})();`);
// Overridden Playwright methods
page.goto = (url: string, options) => goToPage(page, url, options, testInfo, originalGoto);

return page;
}

export const test = base.extend<CustomFixtures>({
page: async ({ page }: CustomTestArgs, use: (r: E2EPage) => Promise<void>, testInfo: TestInfo) => {
page = await extendPageFixture(page, testInfo);
await use(page);
},
});
6 changes: 5 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"baseUrl": ".",
"strict": true,
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
Expand All @@ -15,7 +16,10 @@
"noUnusedLocals": true,
"noUnusedParameters": true,
"jsx": "react",
"jsxFactory": "h"
"jsxFactory": "h",
"paths": {
"@utils/*": ["src/utils/*"]
}
},
"include": [
"src"
Expand Down