@d-zero/puppeteer-page-scan 4.3.4 → 4.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.
package/README.md CHANGED
@@ -46,6 +46,11 @@ const parsedSizes = parseDevicesOption(['desktop', 'tablet']);
46
46
  - ページのリロード
47
47
  - 任意のフック処理
48
48
  - ログインなどの事前処理
49
+ - disclosure要素の展開(オプション)
50
+ - すべての`<details>`要素を開き、すべての`button[aria-expanded="false"]`要素をクリック
51
+ - 新しい要素が見つからなくなるまで繰り返し処理(最大1000回)
52
+ - 各イテレーション後に500ms待機
53
+ - 最大イテレーション数に達した場合はErrorを投げる
49
54
  - ページ全体をスクロール
50
55
 
51
56
  などを行い、スキャンに必要な状態を整えるためのヘルパー関数です。
@@ -73,5 +78,6 @@ await beforePageScan(page, 'https://example.com', {
73
78
  await page.click('button[type="submit"]');
74
79
  },
75
80
  ],
81
+ openDisclosures: true, // オプション: disclosure要素を展開(<details>とbutton[aria-expanded="false"])
76
82
  });
77
83
  ```
@@ -6,6 +6,7 @@ type Options = {
6
6
  hooks?: readonly PageHook[];
7
7
  listener?: Listener<PageScanPhase>;
8
8
  timeout?: number;
9
+ openDisclosures?: boolean;
9
10
  } & Size;
10
11
  /**
11
12
  *
@@ -1,4 +1,54 @@
1
1
  import { scrollAllOver } from '@d-zero/puppeteer-scroll';
2
+ /**
3
+ * Open all disclosure elements on the page
4
+ * This function loops until all disclosure elements are expanded,
5
+ * including nested elements and dynamically-created buttons.
6
+ * @param page
7
+ * @returns The total number of elements opened (details + buttons)
8
+ * @throws {Error} if the maximum iterations (1000) is reached
9
+ */
10
+ async function openAllDisclosures(page) {
11
+ const maxIterations = 1000; // Maximum iterations to prevent infinite loops
12
+ let totalDetails = 0;
13
+ let totalButtons = 0;
14
+ let iteration = 0;
15
+ while (iteration < maxIterations) {
16
+ const result = await page.evaluate(() => {
17
+ // Open all <details> elements
18
+ const detailsElements = document.querySelectorAll('details:not([open])');
19
+ for (const details of detailsElements) {
20
+ details.open = true;
21
+ }
22
+ // Click all collapsed buttons
23
+ const collapsedButtons = document.querySelectorAll('button[aria-expanded="false"]');
24
+ for (const button of collapsedButtons) {
25
+ button.click();
26
+ }
27
+ return {
28
+ details: detailsElements.length,
29
+ buttons: collapsedButtons.length,
30
+ };
31
+ });
32
+ totalDetails += result.details;
33
+ totalButtons += result.buttons;
34
+ // If no elements were opened in this iteration, we're done
35
+ if (result.details === 0 && result.buttons === 0) {
36
+ break;
37
+ }
38
+ // Wait for animations and content rendering before next iteration
39
+ await new Promise((resolve) => setTimeout(resolve, 500));
40
+ iteration++;
41
+ }
42
+ // If we reached the max iterations, throw an error
43
+ if (iteration === maxIterations) {
44
+ throw new Error(`openAllDisclosures: Reached maximum iterations (${maxIterations}). ` +
45
+ `This may indicate an infinite loop caused by dynamically generated disclosure elements.`);
46
+ }
47
+ return {
48
+ details: totalDetails,
49
+ buttons: totalButtons,
50
+ };
51
+ }
2
52
  /**
3
53
  *
4
54
  * @param page
@@ -10,7 +60,7 @@ export async function beforePageScan(page, url, options) {
10
60
  const name = options?.name ?? 'default';
11
61
  const width = options?.width ?? 1400;
12
62
  const resolution = options?.resolution;
13
- const timeout = options?.timeout || 30_000;
63
+ const timeout = options?.timeout || 5000;
14
64
  const countDownId = `${name}${url}_timeout`;
15
65
  listener?.('setViewport', { name, width, resolution });
16
66
  await page.setViewport({
@@ -36,6 +86,14 @@ export async function beforePageScan(page, url, options) {
36
86
  log: (message) => listener?.('hook', { name, message }),
37
87
  });
38
88
  }
89
+ if (options?.openDisclosures) {
90
+ listener?.('hook', { name, message: 'Opening all disclosures...' });
91
+ const result = await openAllDisclosures(page);
92
+ listener?.('hook', {
93
+ name,
94
+ message: `Opened ${result.details} <details> elements and clicked ${result.buttons} [aria-expanded="false"] buttons`,
95
+ });
96
+ }
39
97
  listener?.('scroll', {
40
98
  name,
41
99
  scrollY: 0,
@@ -74,10 +132,10 @@ async function navigateWithFallback(page, url, timeout, isReload, listener, name
74
132
  });
75
133
  // Retry with networkidle2 (more lenient)
76
134
  if (isReload) {
77
- await page.reload({ waitUntil: 'networkidle2', timeout });
135
+ await page.reload({ waitUntil: 'networkidle2', timeout: timeout * 3 });
78
136
  }
79
137
  else {
80
- await page.goto(url, { waitUntil: 'networkidle2', timeout });
138
+ await page.goto(url, { waitUntil: 'networkidle2', timeout: timeout * 3 });
81
139
  }
82
140
  }
83
141
  else {
@@ -5,7 +5,7 @@ export const pageScanLoggers = (log) => ({
5
5
  },
6
6
  load({ type, timeout, id }) {
7
7
  const timeoutSec = Math.floor(timeout / 1000);
8
- log(`%earth% ${type === 'open' ? 'Open' : 'Reload'} page / Timeout: %countdown(${timeoutSec},${id}_timeout,s)%s/${timeoutSec}s`);
8
+ log(`%earth% ${type === 'open' ? 'Open' : 'Reload'} page / Timeout: %countdown(${timeout},${id}_timeout,s)%s/${timeoutSec}s`);
9
9
  },
10
10
  hook({ message }) {
11
11
  log(message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d-zero/puppeteer-page-scan",
3
- "version": "4.3.4",
3
+ "version": "4.4.0",
4
4
  "description": "Scanning page function for puppeteer",
5
5
  "author": "D-ZERO",
6
6
  "license": "MIT",
@@ -24,13 +24,13 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@d-zero/puppeteer-general-actions": "1.2.1",
27
- "@d-zero/puppeteer-scroll": "3.1.7"
27
+ "@d-zero/puppeteer-scroll": "3.1.8"
28
28
  },
29
29
  "devDependencies": {
30
- "puppeteer": "24.34.0"
30
+ "puppeteer": "24.36.0"
31
31
  },
32
32
  "peerDependencies": {
33
- "puppeteer": "24.34.0"
33
+ "puppeteer": "24.36.0"
34
34
  },
35
- "gitHead": "c976f890ac4225e20df8fc83118f31231bd6323c"
35
+ "gitHead": "e2189e6878674b8fef5fa3c121ad109c448040fe"
36
36
  }