@thisisagile/easy-puppeteer 20.1.1 → 21.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.
package/README.md CHANGED
@@ -280,3 +280,14 @@ The `@requires` decorator group also contains `@requires.labCoat()` this decorat
280
280
 
281
281
  To use these decorators, the generic `security` middleware must be loaded first. In most cases, it should be added to the `pre` list of handlers of a service.
282
282
  The security middleware takes an optional configuration object, which is documented [in code](packages/easy-express/src/express/SecurityHandler.ts).
283
+
284
+ ## Side effects and tree shaking
285
+ Every package declares `sideEffects` in its `package.json`, so a bundler only keeps the modules a consumer actually uses. Contributors keep that declaration true by following these rules:
286
+
287
+ - **No module-level mutation of anything outside the module.** Do not patch globals, prototypes or imported objects when a module loads. If something must be configured, configure it in the module that needs it, so it is kept exactly when that module is used.
288
+ - **Catalogue values are pure factories.** Build enum and catalogue values with plain constructors or factory calls that only create objects, so an unused value can be dropped.
289
+ - **A module that must run for its effect on import is listed explicitly.** `easy-test` lists its index and matcher files because `import '@thisisagile/easy-test'` exists to call `expect.extend`. `easy-test-react` lists its index because importing it mocks the timezone. Runtime packages declare `false`.
290
+ - **`DateTime` and `Meta` are deliberately not listed.** `DateTime` extends Day.js and `Meta` loads `reflect-metadata` at module level, but only their own exports need that setup. Bundlers keep a module's top-level code whenever any of its exports are used, so they stay correct unlisted. Listing them would make every root import, such as `isDefined`, pay for Day.js and `reflect-metadata`.
291
+ - **No `emitDecoratorMetadata`.** Nothing in easy reads `design:` metadata; decorators store what they need under their own keys.
292
+
293
+ `test/bundle/workspaces.mjs` checks these rules against the packed packages.
@@ -1,12 +1,7 @@
1
- import { Id, UseCase } from "@thisisagile/easy";
2
1
  import { Browser, Page } from "puppeteer";
3
- import { TestElement, Tester } from "@thisisagile/easy-test-web";
2
+ import { ClosableBrowserTester, TestElement, Tester } from "@thisisagile/easy-test-web";
4
3
  //#region src/PuppeteerTester.d.ts
5
- export declare class PuppeteerTester implements Tester {
6
- host: string;
7
- private readonly browser;
8
- private readonly page;
9
- constructor(host: string, browser: Browser, page: Page);
4
+ export declare class PuppeteerTester extends ClosableBrowserTester<Page> implements Tester {
10
5
  get url(): string;
11
6
  static launch: (headless?: boolean, launchProps?: {}) => Promise<Browser>;
12
7
  static init(host?: string, headless?: boolean, width?: number, height?: number, launchProps?: {}): Promise<Tester>;
@@ -17,10 +12,6 @@ export declare class PuppeteerTester implements Tester {
17
12
  by(key: string, value: string): TestElement;
18
13
  submit(): TestElement;
19
14
  row(contains: string): TestElement;
20
- redirect(url: string): Promise<boolean>;
21
- goto(to: UseCase, id?: Id): Promise<boolean>;
22
- wait(): Promise<boolean>;
23
- close(): Promise<void>;
24
15
  private byXPath;
25
16
  }
26
17
  //#endregion
@@ -1,17 +1,9 @@
1
1
  import { PuppeteerElement } from "./PuppeteerElement.js";
2
2
  import { ctx } from "@thisisagile/easy";
3
3
  import puppeteer from "puppeteer";
4
- import { toUrl } from "@thisisagile/easy-test-web";
4
+ import { ClosableBrowserTester } from "@thisisagile/easy-test-web";
5
5
  //#region src/PuppeteerTester.ts
6
- var PuppeteerTester = class PuppeteerTester {
7
- host;
8
- browser;
9
- page;
10
- constructor(host, browser, page) {
11
- this.host = host;
12
- this.browser = browser;
13
- this.page = page;
14
- }
6
+ var PuppeteerTester = class PuppeteerTester extends ClosableBrowserTester {
15
7
  get url() {
16
8
  return this.page.url();
17
9
  }
@@ -55,18 +47,6 @@ var PuppeteerTester = class PuppeteerTester {
55
47
  row(contains) {
56
48
  return this.byXPath(`//tr[contains(., '${contains}')]`);
57
49
  }
58
- redirect(url) {
59
- return this.page.goto(url).then((r) => r?.ok() ?? false);
60
- }
61
- goto(to, id) {
62
- return this.redirect(toUrl(to, this.host, id));
63
- }
64
- wait() {
65
- return this.page.waitForNavigation().then((r) => r?.ok() || false);
66
- }
67
- close() {
68
- return this.browser.close();
69
- }
70
50
  byXPath(q) {
71
51
  const h = this.page.waitForSelector(`xpath/.${q}`);
72
52
  return new PuppeteerElement(h);
@@ -1 +1 @@
1
- {"version":3,"file":"PuppeteerTester.js","names":[],"sources":["../src/PuppeteerTester.ts"],"sourcesContent":["import { PuppeteerElement } from './PuppeteerElement';\nimport puppeteer, { Browser, ElementHandle, Page } from 'puppeteer';\nimport { ctx, Id, UseCase } from '@thisisagile/easy';\nimport { TestElement, Tester, toUrl } from '@thisisagile/easy-test-web';\n\nexport class PuppeteerTester implements Tester {\n constructor(public host: string, private readonly browser: Browser, private readonly page: Page) {}\n\n get url(): string {\n return this.page.url();\n }\n\n /* istanbul ignore next */\n static launch = (headless = true, launchProps = {}): Promise<Browser> =>\n puppeteer.launch({ headless: headless ? 'shell' : false, args: ['--no-sandbox', '--start-maximized'], ...launchProps });\n\n /* istanbul ignore next */\n static async init(host: string = ctx.env.get('webHost', '') as string, headless = true, width = 1200, height = 800, launchProps = {}): Promise<Tester> {\n const browser = await PuppeteerTester.launch(headless, launchProps);\n const page = await browser.newPage();\n await page.setViewport({ width, height });\n return new PuppeteerTester(host, browser, page);\n }\n\n byClass(c: string): TestElement {\n const h = this.page.waitForSelector(`.${c}`);\n return new PuppeteerElement(h);\n }\n\n byId(id: string): TestElement {\n const h = this.page.waitForSelector(`#${id}`);\n return new PuppeteerElement(h);\n }\n\n byDataTestId(id: string): TestElement {\n return this.by('data-testid', id);\n }\n\n byName(name: string): TestElement {\n return this.by('name', name);\n }\n\n by(key: string, value: string): TestElement {\n const h = this.page.waitForSelector(`[${key}=${value}]`);\n return new PuppeteerElement(h);\n }\n\n submit(): TestElement {\n return this.by('type', 'submit');\n }\n\n row(contains: string): TestElement {\n // Select the first row that contains the given input. byXPath prefixes the query with a dot,\n // so it must stay relative: '(//tr[...])[1]' would become '.(//tr[...])[1]', which is not\n // valid xpath - it matches nothing and only times out. waitForSelector takes the first match.\n return this.byXPath(`//tr[contains(., '${contains}')]`);\n }\n\n redirect(url: string): Promise<boolean> {\n return this.page.goto(url).then(r => r?.ok() ?? false);\n }\n\n goto(to: UseCase, id?: Id): Promise<boolean> {\n return this.redirect(toUrl(to, this.host, id));\n }\n\n wait(): Promise<boolean> {\n return this.page.waitForNavigation().then(r => r?.ok() || false);\n }\n\n close(): Promise<void> {\n return this.browser.close();\n }\n\n private byXPath(q: string): TestElement {\n const h = this.page.waitForSelector(`xpath/.${q}`);\n return new PuppeteerElement(h as Promise<ElementHandle<Element>>);\n }\n}\n"],"mappings":";;;;;AAKA,IAAa,kBAAb,MAAa,gBAAkC;CAC1B;CAA+B;CAAmC;CAArF,YAAY,MAAqB,SAAmC,MAA6B;EAA9E,KAAA,OAAA;EAA+B,KAAA,UAAA;EAAmC,KAAA,OAAA;CAAa;CAElG,IAAI,MAAc;EAChB,OAAO,KAAK,KAAK,IAAI;CACvB;;CAGA,OAAO,UAAU,WAAW,MAAM,cAAc,CAAC,MAC/C,UAAU,OAAO;EAAE,UAAU,WAAW,UAAU;EAAO,MAAM,CAAC,gBAAgB,mBAAmB;EAAG,GAAG;CAAY,CAAC;;CAGxH,aAAa,KAAK,OAAe,IAAI,IAAI,IAAI,WAAW,EAAE,GAAa,WAAW,MAAM,QAAQ,MAAM,SAAS,KAAK,cAAc,CAAC,GAAoB;EACrJ,MAAM,UAAU,MAAM,gBAAgB,OAAO,UAAU,WAAW;EAClE,MAAM,OAAO,MAAM,QAAQ,QAAQ;EACnC,MAAM,KAAK,YAAY;GAAE;GAAO;EAAO,CAAC;EACxC,OAAO,IAAI,gBAAgB,MAAM,SAAS,IAAI;CAChD;CAEA,QAAQ,GAAwB;EAC9B,MAAM,IAAI,KAAK,KAAK,gBAAgB,IAAI,GAAG;EAC3C,OAAO,IAAI,iBAAiB,CAAC;CAC/B;CAEA,KAAK,IAAyB;EAC5B,MAAM,IAAI,KAAK,KAAK,gBAAgB,IAAI,IAAI;EAC5C,OAAO,IAAI,iBAAiB,CAAC;CAC/B;CAEA,aAAa,IAAyB;EACpC,OAAO,KAAK,GAAG,eAAe,EAAE;CAClC;CAEA,OAAO,MAA2B;EAChC,OAAO,KAAK,GAAG,QAAQ,IAAI;CAC7B;CAEA,GAAG,KAAa,OAA4B;EAC1C,MAAM,IAAI,KAAK,KAAK,gBAAgB,IAAI,IAAI,GAAG,MAAM,EAAE;EACvD,OAAO,IAAI,iBAAiB,CAAC;CAC/B;CAEA,SAAsB;EACpB,OAAO,KAAK,GAAG,QAAQ,QAAQ;CACjC;CAEA,IAAI,UAA+B;EAIjC,OAAO,KAAK,QAAQ,qBAAqB,SAAS,IAAI;CACxD;CAEA,SAAS,KAA+B;EACtC,OAAO,KAAK,KAAK,KAAK,GAAG,CAAC,CAAC,MAAK,MAAK,GAAG,GAAG,KAAK,KAAK;CACvD;CAEA,KAAK,IAAa,IAA2B;EAC3C,OAAO,KAAK,SAAS,MAAM,IAAI,KAAK,MAAM,EAAE,CAAC;CAC/C;CAEA,OAAyB;EACvB,OAAO,KAAK,KAAK,kBAAkB,CAAC,CAAC,MAAK,MAAK,GAAG,GAAG,KAAK,KAAK;CACjE;CAEA,QAAuB;EACrB,OAAO,KAAK,QAAQ,MAAM;CAC5B;CAEA,QAAgB,GAAwB;EACtC,MAAM,IAAI,KAAK,KAAK,gBAAgB,UAAU,GAAG;EACjD,OAAO,IAAI,iBAAiB,CAAoC;CAClE;AACF"}
1
+ {"version":3,"file":"PuppeteerTester.js","names":[],"sources":["../src/PuppeteerTester.ts"],"sourcesContent":["import { PuppeteerElement } from './PuppeteerElement';\nimport puppeteer, { Browser, ElementHandle, Page } from 'puppeteer';\nimport { ctx } from '@thisisagile/easy';\nimport { ClosableBrowserTester, TestElement, Tester } from '@thisisagile/easy-test-web';\n\nexport class PuppeteerTester extends ClosableBrowserTester<Page> implements Tester {\n get url(): string {\n return this.page.url();\n }\n\n /* istanbul ignore next */\n static launch = (headless = true, launchProps = {}): Promise<Browser> =>\n puppeteer.launch({ headless: headless ? 'shell' : false, args: ['--no-sandbox', '--start-maximized'], ...launchProps });\n\n /* istanbul ignore next */\n static async init(host: string = ctx.env.get('webHost', '') as string, headless = true, width = 1200, height = 800, launchProps = {}): Promise<Tester> {\n const browser = await PuppeteerTester.launch(headless, launchProps);\n const page = await browser.newPage();\n await page.setViewport({ width, height });\n return new PuppeteerTester(host, browser, page);\n }\n\n byClass(c: string): TestElement {\n const h = this.page.waitForSelector(`.${c}`);\n return new PuppeteerElement(h);\n }\n\n byId(id: string): TestElement {\n const h = this.page.waitForSelector(`#${id}`);\n return new PuppeteerElement(h);\n }\n\n byDataTestId(id: string): TestElement {\n return this.by('data-testid', id);\n }\n\n byName(name: string): TestElement {\n return this.by('name', name);\n }\n\n by(key: string, value: string): TestElement {\n const h = this.page.waitForSelector(`[${key}=${value}]`);\n return new PuppeteerElement(h);\n }\n\n submit(): TestElement {\n return this.by('type', 'submit');\n }\n\n row(contains: string): TestElement {\n // Select the first row that contains the given input. byXPath prefixes the query with a dot,\n // so it must stay relative: '(//tr[...])[1]' would become '.(//tr[...])[1]', which is not\n // valid xpath - it matches nothing and only times out. waitForSelector takes the first match.\n return this.byXPath(`//tr[contains(., '${contains}')]`);\n }\n\n private byXPath(q: string): TestElement {\n const h = this.page.waitForSelector(`xpath/.${q}`);\n return new PuppeteerElement(h as Promise<ElementHandle<Element>>);\n }\n}\n"],"mappings":";;;;;AAKA,IAAa,kBAAb,MAAa,wBAAwB,sBAA8C;CACjF,IAAI,MAAc;EAChB,OAAO,KAAK,KAAK,IAAI;CACvB;;CAGA,OAAO,UAAU,WAAW,MAAM,cAAc,CAAC,MAC/C,UAAU,OAAO;EAAE,UAAU,WAAW,UAAU;EAAO,MAAM,CAAC,gBAAgB,mBAAmB;EAAG,GAAG;CAAY,CAAC;;CAGxH,aAAa,KAAK,OAAe,IAAI,IAAI,IAAI,WAAW,EAAE,GAAa,WAAW,MAAM,QAAQ,MAAM,SAAS,KAAK,cAAc,CAAC,GAAoB;EACrJ,MAAM,UAAU,MAAM,gBAAgB,OAAO,UAAU,WAAW;EAClE,MAAM,OAAO,MAAM,QAAQ,QAAQ;EACnC,MAAM,KAAK,YAAY;GAAE;GAAO;EAAO,CAAC;EACxC,OAAO,IAAI,gBAAgB,MAAM,SAAS,IAAI;CAChD;CAEA,QAAQ,GAAwB;EAC9B,MAAM,IAAI,KAAK,KAAK,gBAAgB,IAAI,GAAG;EAC3C,OAAO,IAAI,iBAAiB,CAAC;CAC/B;CAEA,KAAK,IAAyB;EAC5B,MAAM,IAAI,KAAK,KAAK,gBAAgB,IAAI,IAAI;EAC5C,OAAO,IAAI,iBAAiB,CAAC;CAC/B;CAEA,aAAa,IAAyB;EACpC,OAAO,KAAK,GAAG,eAAe,EAAE;CAClC;CAEA,OAAO,MAA2B;EAChC,OAAO,KAAK,GAAG,QAAQ,IAAI;CAC7B;CAEA,GAAG,KAAa,OAA4B;EAC1C,MAAM,IAAI,KAAK,KAAK,gBAAgB,IAAI,IAAI,GAAG,MAAM,EAAE;EACvD,OAAO,IAAI,iBAAiB,CAAC;CAC/B;CAEA,SAAsB;EACpB,OAAO,KAAK,GAAG,QAAQ,QAAQ;CACjC;CAEA,IAAI,UAA+B;EAIjC,OAAO,KAAK,QAAQ,qBAAqB,SAAS,IAAI;CACxD;CAEA,QAAgB,GAAwB;EACtC,MAAM,IAAI,KAAK,KAAK,gBAAgB,UAAU,GAAG;EACjD,OAAO,IAAI,iBAAiB,CAAoC;CAClE;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thisisagile/easy-puppeteer",
3
- "version": "20.1.1",
3
+ "version": "21.0.0",
4
4
  "description": "Straightforward wrapper library for Puppeteer web testing framework",
5
5
  "author": "Sander Hoogendoorn",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  "scripts": {
25
25
  "lint": "yarn g:eslint . --ext .js,.jsx,.ts,.tsx --fix",
26
26
  "format": "yarn g:prettier --check --write src test *.json",
27
- "build": "yarn g:tsdown && yarn g:exports",
27
+ "build": "yarn g:tsdown",
28
28
  "test": "yarn g:jest --coverage",
29
29
  "prepack": "yarn g:copy-readme"
30
30
  },
@@ -42,8 +42,8 @@
42
42
  "@thisisagile/easy-test": "*"
43
43
  },
44
44
  "dependencies": {
45
- "@thisisagile/easy": "^20.1.1",
46
- "@thisisagile/easy-test-web": "^20.1.1",
45
+ "@thisisagile/easy": "^21.0.0",
46
+ "@thisisagile/easy-test-web": "^21.0.0",
47
47
  "puppeteer": "^24.43.1"
48
48
  },
49
49
  "exports": {
@@ -1,11 +1,9 @@
1
1
  import { PuppeteerElement } from './PuppeteerElement';
2
2
  import puppeteer, { Browser, ElementHandle, Page } from 'puppeteer';
3
- import { ctx, Id, UseCase } from '@thisisagile/easy';
4
- import { TestElement, Tester, toUrl } from '@thisisagile/easy-test-web';
5
-
6
- export class PuppeteerTester implements Tester {
7
- constructor(public host: string, private readonly browser: Browser, private readonly page: Page) {}
3
+ import { ctx } from '@thisisagile/easy';
4
+ import { ClosableBrowserTester, TestElement, Tester } from '@thisisagile/easy-test-web';
8
5
 
6
+ export class PuppeteerTester extends ClosableBrowserTester<Page> implements Tester {
9
7
  get url(): string {
10
8
  return this.page.url();
11
9
  }
@@ -56,22 +54,6 @@ export class PuppeteerTester implements Tester {
56
54
  return this.byXPath(`//tr[contains(., '${contains}')]`);
57
55
  }
58
56
 
59
- redirect(url: string): Promise<boolean> {
60
- return this.page.goto(url).then(r => r?.ok() ?? false);
61
- }
62
-
63
- goto(to: UseCase, id?: Id): Promise<boolean> {
64
- return this.redirect(toUrl(to, this.host, id));
65
- }
66
-
67
- wait(): Promise<boolean> {
68
- return this.page.waitForNavigation().then(r => r?.ok() || false);
69
- }
70
-
71
- close(): Promise<void> {
72
- return this.browser.close();
73
- }
74
-
75
57
  private byXPath(q: string): TestElement {
76
58
  const h = this.page.waitForSelector(`xpath/.${q}`);
77
59
  return new PuppeteerElement(h as Promise<ElementHandle<Element>>);