@rvoh/psychic-spec-helpers 3.3.0 → 3.4.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.
@@ -1,7 +1,14 @@
1
1
  import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js';
2
2
  export default async function toHaveSelector(page, selector, opts) {
3
+ // Presence-only contract: this matcher asserts that the selector is
4
+ // attached to the DOM, regardless of visibility. Strip visible/hidden so
5
+ // a caller-passed { visible: true } can't narrow the check to also
6
+ // require visibility.
7
+ const waitForOpts = applyDefaultWaitForOpts(opts);
8
+ delete waitForOpts.visible;
9
+ delete waitForOpts.hidden;
3
10
  try {
4
- await page.waitForSelector(selector, applyDefaultWaitForOpts(opts));
11
+ await page.waitForSelector(selector, waitForOpts);
5
12
  return {
6
13
  pass: true,
7
14
  message: () => {
@@ -1,18 +1,35 @@
1
1
  import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js';
2
+ import sleep from '../../shared/sleep.js';
2
3
  export default async function toNotHaveSelector(page, selector, opts) {
3
- try {
4
- await page.waitForSelector(selector, { hidden: true, ...applyDefaultWaitForOpts(opts) });
5
- return {
6
- pass: true,
7
- message: () => {
8
- throw new Error('Cannot negate toNotHaveSelector, use toHaveSelector instead');
9
- },
10
- };
11
- }
12
- catch {
13
- return {
14
- pass: false,
15
- message: () => `Expected page to not have visible selector, but it did: ${selector}`,
16
- };
4
+ // Presence-only contract: this matcher asserts that the selector is
5
+ // absent from the DOM, regardless of visibility. A present-but-hidden
6
+ // element must FAIL this matcher, so we poll for true DOM absence via
7
+ // page.$ (rather than relying on waitForSelector({ hidden: true }),
8
+ // which only asserts invisibility). page.$ (not document.querySelector)
9
+ // is used so Puppeteer's extended selectors like ::-p-text keep working.
10
+ const timeout = applyDefaultWaitForOpts(opts).timeout ?? 5000;
11
+ const interval = 50;
12
+ const startTime = Date.now();
13
+ async function poll() {
14
+ const element = await page.$(selector);
15
+ if (!element) {
16
+ return {
17
+ pass: true,
18
+ message: () => {
19
+ throw new Error('Cannot negate toNotHaveSelector, use toHaveSelector instead');
20
+ },
21
+ };
22
+ }
23
+ if (Date.now() >= startTime + timeout) {
24
+ await element.dispose();
25
+ return {
26
+ pass: false,
27
+ message: () => `Expected page to not have selector, but it did: ${selector}`,
28
+ };
29
+ }
30
+ await element.dispose();
31
+ await sleep(interval);
32
+ return await poll();
17
33
  }
34
+ return await poll();
18
35
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@rvoh/psychic-spec-helpers",
4
- "version": "3.3.0",
4
+ "version": "3.4.0",
5
5
  "description": "psychic framework spec helpers",
6
6
  "author": "RVO Health",
7
7
  "publishConfig": {
@@ -6,8 +6,16 @@ export default async function toHaveSelector(
6
6
  selector: string,
7
7
  opts?: WaitForSelectorOptions
8
8
  ) {
9
+ // Presence-only contract: this matcher asserts that the selector is
10
+ // attached to the DOM, regardless of visibility. Strip visible/hidden so
11
+ // a caller-passed { visible: true } can't narrow the check to also
12
+ // require visibility.
13
+ const waitForOpts = applyDefaultWaitForOpts(opts)
14
+ delete waitForOpts.visible
15
+ delete waitForOpts.hidden
16
+
9
17
  try {
10
- await page.waitForSelector(selector, applyDefaultWaitForOpts(opts))
18
+ await page.waitForSelector(selector, waitForOpts)
11
19
  return {
12
20
  pass: true,
13
21
  message: () => {
@@ -1,23 +1,45 @@
1
1
  import { Page, WaitForSelectorOptions } from 'puppeteer'
2
2
  import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js'
3
+ import sleep from '../../shared/sleep.js'
3
4
 
4
5
  export default async function toNotHaveSelector(
5
6
  page: Page,
6
7
  selector: string,
7
8
  opts?: WaitForSelectorOptions
8
9
  ) {
9
- try {
10
- await page.waitForSelector(selector, { hidden: true, ...applyDefaultWaitForOpts(opts) })
11
- return {
12
- pass: true,
13
- message: () => {
14
- throw new Error('Cannot negate toNotHaveSelector, use toHaveSelector instead')
15
- },
10
+ // Presence-only contract: this matcher asserts that the selector is
11
+ // absent from the DOM, regardless of visibility. A present-but-hidden
12
+ // element must FAIL this matcher, so we poll for true DOM absence via
13
+ // page.$ (rather than relying on waitForSelector({ hidden: true }),
14
+ // which only asserts invisibility). page.$ (not document.querySelector)
15
+ // is used so Puppeteer's extended selectors like ::-p-text keep working.
16
+ const timeout = applyDefaultWaitForOpts(opts).timeout ?? 5000
17
+ const interval = 50
18
+ const startTime = Date.now()
19
+
20
+ async function poll(): Promise<{ pass: boolean; message: () => string }> {
21
+ const element = await page.$(selector)
22
+ if (!element) {
23
+ return {
24
+ pass: true,
25
+ message: () => {
26
+ throw new Error('Cannot negate toNotHaveSelector, use toHaveSelector instead')
27
+ },
28
+ }
16
29
  }
17
- } catch {
18
- return {
19
- pass: false,
20
- message: () => `Expected page to not have visible selector, but it did: ${selector}`,
30
+
31
+ if (Date.now() >= startTime + timeout) {
32
+ await element.dispose()
33
+ return {
34
+ pass: false,
35
+ message: () => `Expected page to not have selector, but it did: ${selector}`,
36
+ }
21
37
  }
38
+
39
+ await element.dispose()
40
+ await sleep(interval)
41
+ return await poll()
22
42
  }
43
+
44
+ return await poll()
23
45
  }