@atomicsmash/test-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ import type { Page, BrowserContext, PlaywrightTestConfig } from "@playwright/test";
2
+ import type { SerialFrameSelector } from "axe-core";
3
+ export type PageList = {
4
+ url: string;
5
+ name: string;
6
+ slug?: string;
7
+ restrictedAccess?: boolean;
8
+ }[];
9
+ export type AuthenticatePageFunction = ({ page }: {
10
+ page: Page;
11
+ }, use: (r: Page) => Promise<void>) => Promise<void>;
12
+ export declare const playwrightProjectsConfig: PlaywrightTestConfig["projects"];
13
+ export declare const getLighthouseTest: (logInUser: AuthenticatePageFunction) => import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
14
+ authenticatedPage: Page;
15
+ context: BrowserContext;
16
+ }, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions & {
17
+ port: number;
18
+ }>;
19
+ type LighthouseTestFunction = Parameters<ReturnType<typeof getLighthouseTest>>[1];
20
+ export declare const doLighthouseTest: (pageToTest: PageList[number], type: "desktop" | "mobile") => LighthouseTestFunction;
21
+ export declare function checkAccessibility(page: Page, { includes, excludes, }?: {
22
+ includes?: SerialFrameSelector[];
23
+ excludes?: SerialFrameSelector[];
24
+ }): Promise<void>;
25
+ export declare const checkPrivilegedPages: (pagesToTest: PageList) => ({ page }: {
26
+ page: Page;
27
+ }) => Promise<void>;
28
+ export declare const checkForLoremIpsum: (pagesToTest: PageList) => ({ authenticatedPage }: {
29
+ authenticatedPage: Page;
30
+ }) => Promise<void>;
31
+ export {};
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,IAAI,EACJ,cAAc,EACd,oBAAoB,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAapD,MAAM,MAAM,QAAQ,GAAG;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B,EAAE,CAAC;AAEJ,MAAM,MAAM,wBAAwB,GAAG,CACtC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,EACxB,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,KAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,eAAO,MAAM,wBAAwB,EAAE,oBAAoB,CAAC,UAAU,CA2BrE,CAAC;AAEF,eAAO,MAAM,iBAAiB,cAAe,wBAAwB;uBAE9C,IAAI;aAAW,cAAc;;UAC1C,MAAM;EA2Bf,CAAC;AAEF,KAAK,sBAAsB,GAAG,UAAU,CACvC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CACpC,CAAC,CAAC,CAAC,CAAC;AAEL,eAAO,MAAM,gBAAgB,EAAE,CAC9B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC5B,IAAI,EAAE,SAAS,GAAG,QAAQ,KACtB,sBA6BH,CAAC;AAEH,wBAAsB,kBAAkB,CACvC,IAAI,EAAE,IAAI,EACV,EACC,QAAQ,EACR,QAAQ,GACR,GAAE;IACF,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC5B,iBA6BN;AAED,eAAO,MAAM,oBAAoB;UAEP,IAAI;mBAS5B,CAAC;AACH,eAAO,MAAM,kBAAkB;uBAEqB,IAAI;mBAStD,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,140 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { AxeBuilder } from "@axe-core/playwright";
4
+ import { test as base, chromium, devices } from "@playwright/test";
5
+ import getPort from "get-port";
6
+ import lighthouseDesktopConfig from "lighthouse/core/config/lr-desktop-config.js";
7
+ import lighthouseMobileConfig from "lighthouse/core/config/lr-mobile-config.js";
8
+ import { playAudit } from "playwright-lighthouse";
9
+ const { expect } = base;
10
+ export const playwrightProjectsConfig = [
11
+ {
12
+ name: "chromium",
13
+ use: { ...devices["Desktop Chrome"] },
14
+ grepInvert: /@mobile @lighthouse/,
15
+ },
16
+ {
17
+ name: "firefox",
18
+ use: { ...devices["Desktop Firefox"] },
19
+ grepInvert: /@lighthouse/,
20
+ },
21
+ {
22
+ name: "webkit",
23
+ use: { ...devices["Desktop Safari"] },
24
+ grepInvert: /@lighthouse/,
25
+ },
26
+ /* Test against mobile viewports. */
27
+ {
28
+ name: "Mobile Chrome",
29
+ use: { ...devices["Pixel 5"] },
30
+ grepInvert: /@desktop @lighthouse/,
31
+ },
32
+ {
33
+ name: "Mobile Safari",
34
+ use: { ...devices["iPhone 12"] },
35
+ grepInvert: /@lighthouse/,
36
+ },
37
+ ];
38
+ export const getLighthouseTest = (logInUser) => {
39
+ const lighthouseTest = base.extend({
40
+ port: [
41
+ // eslint-disable-next-line no-empty-pattern
42
+ async ({}, use) => {
43
+ // Assign a unique port for each playwright worker to support parallel tests
44
+ const port = await getPort();
45
+ await use(port);
46
+ },
47
+ { scope: "worker" },
48
+ ],
49
+ context: [
50
+ async ({ port }, use) => {
51
+ const userDataDir = path.join(os.tmpdir(), "pw", String(Math.random()));
52
+ const context = await chromium.launchPersistentContext(userDataDir, {
53
+ args: [`--remote-debugging-port=${port}`],
54
+ });
55
+ await use(context);
56
+ await context.close();
57
+ },
58
+ { scope: "test" },
59
+ ],
60
+ authenticatedPage: [logInUser, { scope: "test" }],
61
+ });
62
+ lighthouseTest.describe.configure({ mode: "parallel", retries: 3 });
63
+ return lighthouseTest;
64
+ };
65
+ export const doLighthouseTest = (pageToTest, type) => async function ({ port, authenticatedPage }) {
66
+ await authenticatedPage.goto(pageToTest.url);
67
+ await playAudit({
68
+ page: authenticatedPage,
69
+ thresholds: {
70
+ performance: 85,
71
+ accessibility: 85,
72
+ "best-practices": 85,
73
+ seo: 85,
74
+ },
75
+ port: port,
76
+ reports: {
77
+ formats: {
78
+ html: true,
79
+ },
80
+ directory: "lighthouse/latest-lighthouse-report",
81
+ name: `${pageToTest.slug ?? slugify(pageToTest.name)}-${type}`,
82
+ },
83
+ config: (type === "desktop"
84
+ ? lighthouseDesktopConfig
85
+ : lighthouseMobileConfig),
86
+ ignoreError: true,
87
+ });
88
+ await authenticatedPage.goto(`file://${process.cwd()}/lighthouse/latest-lighthouse-report/${pageToTest.slug ?? slugify(pageToTest.name)}-${type}.html`);
89
+ };
90
+ export async function checkAccessibility(page, { includes, excludes, } = {}) {
91
+ let builder;
92
+ builder = new AxeBuilder({ page });
93
+ if (includes) {
94
+ for (const include of includes) {
95
+ builder = builder.include(include);
96
+ }
97
+ }
98
+ if (excludes) {
99
+ for (const exclude of excludes) {
100
+ builder = builder.exclude(exclude);
101
+ }
102
+ }
103
+ builder = builder.exclude("#userback_button_container"); // Ignore userback testing tool
104
+ const accessibilityScanResults = await builder.analyze();
105
+ expect(accessibilityScanResults.violations.filter((violation) => {
106
+ if (violation.id === "aria-allowed-attr" &&
107
+ violation.nodes[0].html.startsWith("<button") &&
108
+ violation.nodes[0].html.includes(`role="switch"`)) {
109
+ return false;
110
+ }
111
+ return true;
112
+ })).toEqual([]);
113
+ }
114
+ export const checkPrivilegedPages = (pagesToTest) => async ({ page }) => {
115
+ for (const pageToTest of pagesToTest.filter((page) => page.restrictedAccess)) {
116
+ const response = await page.goto(pageToTest.url);
117
+ expect(response).not.toBeNull();
118
+ const nonNullResponse = response;
119
+ expect(nonNullResponse.status()).toBe(401);
120
+ }
121
+ };
122
+ export const checkForLoremIpsum = (pagesToTest) => async ({ authenticatedPage }) => {
123
+ for (const pageToTest of pagesToTest) {
124
+ await authenticatedPage.goto(pageToTest.url);
125
+ await expect(authenticatedPage.getByText("Lorem ipsum")).not.toBeAttached({
126
+ timeout: 100,
127
+ });
128
+ }
129
+ };
130
+ function slugify(stringToSlugify) {
131
+ // REGEX_1: Match any character that isn't alphanumeric, underscore, whitespace and underscore
132
+ // REGEX_2: Match any character that is underscore, whitespace and underscore
133
+ // REGEX_3: Match any hyphens at the start or the end of the string
134
+ return stringToSlugify
135
+ .toLowerCase()
136
+ .trim()
137
+ .replace(/[^\w\s-]/g, "")
138
+ .replace(/[\s_-]+/g, "-")
139
+ .replace(/^-+|(?<!-)-+$/g, "");
140
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@atomicsmash/test-utils",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "A collection of helper functions for automated testing with Playwright.",
6
+ "keywords": [
7
+ "playwright",
8
+ "testing",
9
+ "automated test",
10
+ "test"
11
+ ],
12
+ "homepage": "https://www.atomicsmash.co.uk/",
13
+ "license": "GPL-3.0-or-later",
14
+ "author": {
15
+ "name": "Atomic Smash",
16
+ "email": "npm@atomicsmash.co.uk",
17
+ "url": "https://www.atomicsmash.co.uk/"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/AtomicSmash/packages",
22
+ "directory": "packages/test-utils"
23
+ },
24
+ "files": [
25
+ "./dist/**/*"
26
+ ],
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "scripts": {
33
+ "dev": "tsc --build --watch tsconfig.build.json",
34
+ "build": "npm-run-all clean compile",
35
+ "clean": "del-cli ./dist/ *.tsbuildinfo",
36
+ "compile": "tsc --build tsconfig.build.json",
37
+ "test": "TZ=\"Europe/Istanbul\" vitest run",
38
+ "test:ui": "TZ=\"Europe/Istanbul\" vitest --ui",
39
+ "test:coverage": "TZ=\"Europe/Istanbul\" vitest run --coverage",
40
+ "lint": "run-p lint:*",
41
+ "lint:eslint": "eslint --cache --cache-location ./node_modules/.cache/eslint . --ext .js,.ts,.jsx,.tsx",
42
+ "lint:types": "tsc"
43
+ },
44
+ "peerDependencies": {
45
+ "@playwright/test": "^1.35.1"
46
+ },
47
+ "dependencies": {
48
+ "@axe-core/playwright": "^4.7.3",
49
+ "get-port": "^7.0.0",
50
+ "playwright-lighthouse": "^3.1.0"
51
+ },
52
+ "devDependencies": {
53
+ "lighthouse": "^11.0.0"
54
+ }
55
+ }