@deot/dev-test 2.0.0 → 2.0.2

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/dist/index.cjs.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const devShared = require('@deot/dev-shared');
6
6
  const childProcess = require('child_process');
7
+ const puppeteer = require('puppeteer');
7
8
 
8
9
  function _interopNamespaceDefault(e) {
9
10
  const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
@@ -135,4 +136,309 @@ class Command {
135
136
  }
136
137
  }
137
138
 
139
+ class Operater {
140
+ page;
141
+ constructor(v) {
142
+ this.page = v;
143
+ }
144
+ /**
145
+ * @param {string} selector ~
146
+ * @param {ClickOptions} options ~
147
+ */
148
+ async click(selector, options) {
149
+ await this.page.click(selector, options);
150
+ }
151
+ /**
152
+ * @param {string} selector ~
153
+ * @returns {number} ~
154
+ */
155
+ async count(selector) {
156
+ return (await this.page.$$(selector)).length;
157
+ }
158
+ /**
159
+ * @param {string} selector ~
160
+ * @returns {Promise<string>} ~
161
+ */
162
+ async text(selector) {
163
+ return await this.page.$eval(selector, (node) => node.textContent);
164
+ }
165
+ /**
166
+ * @param {string} selector ~
167
+ * @returns {Promise<string>} ~
168
+ */
169
+ async value(selector) {
170
+ return await this.page.$eval(selector, (node) => node.value);
171
+ }
172
+ /**
173
+ * @param {string} selector ~
174
+ * @returns {Promise<string>} ~
175
+ */
176
+ async html(selector) {
177
+ return await this.page.$eval(selector, (node) => node.innerHTML);
178
+ }
179
+ /**
180
+ * @param {string} selector ~
181
+ * @returns {Promise<string[]>} ~
182
+ */
183
+ async classList(selector) {
184
+ return await this.page.$eval(selector, (node) => [...node.classList]);
185
+ }
186
+ /**
187
+ * @param {string} selector ~
188
+ * @returns {Promise<HTMLElement[]>} ~
189
+ */
190
+ async children(selector) {
191
+ return await this.page.$eval(selector, (node) => [...node.children]);
192
+ }
193
+ /**
194
+ * @param {string} selector ~
195
+ * @returns {Promise<boolean>} ~
196
+ */
197
+ async isVisible(selector) {
198
+ const display = await this.page.$eval(selector, (node) => {
199
+ return window.getComputedStyle(node).display;
200
+ });
201
+ return display !== "none";
202
+ }
203
+ /**
204
+ *
205
+ * @param {string} selector ~
206
+ * @returns {Promise<boolean>} ~
207
+ */
208
+ async isChecked(selector) {
209
+ return await this.page.$eval(
210
+ selector,
211
+ (node) => node.checked
212
+ );
213
+ }
214
+ /**
215
+ *
216
+ * @param {string} selector ~
217
+ * @returns {Promise<boolean>} ~
218
+ */
219
+ async isFocused(selector) {
220
+ return await this.page.$eval(selector, (node) => node === document.activeElement);
221
+ }
222
+ /**
223
+ * @param {string} selector ~
224
+ * @param {string} value$ ~
225
+ * @returns {Promise<void>} ~
226
+ */
227
+ async setValue(selector, value$) {
228
+ await this.page.$eval(
229
+ selector,
230
+ (node, value$$) => {
231
+ node.value = value$$;
232
+ node.dispatchEvent(new Event("input"));
233
+ },
234
+ value$
235
+ );
236
+ }
237
+ /**
238
+ * @param {string} selector ~
239
+ * @param {string} value$ ~
240
+ * @returns {Promise<void>} ~
241
+ */
242
+ async typeValue(selector, value$) {
243
+ const el = await this.page.$(selector);
244
+ await el.evaluate((node) => node.value = "");
245
+ await el.type(value$);
246
+ }
247
+ /**
248
+ * @param {string} selector ~
249
+ * @param {string} value$ ~
250
+ * @returns {Promise<void>} ~
251
+ */
252
+ async enterValue(selector, value$) {
253
+ const el = await this.page.$(selector);
254
+ await el.evaluate((node) => node.value = "");
255
+ await el.type(value$);
256
+ await el.press("Enter");
257
+ }
258
+ /**
259
+ * @param {string} selector ~
260
+ * @returns {Promise<void>} ~
261
+ */
262
+ async clearValue(selector) {
263
+ return await this.page.$eval(
264
+ selector,
265
+ (node) => {
266
+ node.value = "";
267
+ }
268
+ );
269
+ }
270
+ /**
271
+ *
272
+ * @param {number} time ~
273
+ * @returns {Promise<any>} ~
274
+ */
275
+ sleep(time) {
276
+ return this.page.evaluate((time$) => {
277
+ return new Promise((r) => {
278
+ setTimeout(r, time$);
279
+ });
280
+ }, time);
281
+ }
282
+ nextFrame() {
283
+ return this.page.evaluate(() => {
284
+ return new Promise((resolve) => {
285
+ requestAnimationFrame(() => {
286
+ requestAnimationFrame(resolve);
287
+ });
288
+ });
289
+ });
290
+ }
291
+ }
292
+
293
+ class Launch {
294
+ browser;
295
+ page;
296
+ operater;
297
+ _browser;
298
+ _page;
299
+ options;
300
+ constructor() {
301
+ this.operater = new Proxy({}, {
302
+ get: () => {
303
+ throw new Error("operater is not defined. create* invote first");
304
+ }
305
+ });
306
+ this.browser = new Proxy({}, {
307
+ get: () => {
308
+ throw new Error("browser is not defined. createBrowser invote first");
309
+ }
310
+ });
311
+ this.page = new Proxy({}, {
312
+ get: () => {
313
+ throw new Error("page is not defined. createPage invote first");
314
+ }
315
+ });
316
+ this.options = process.env.CI ? (
317
+ /* istanbul ignore next */
318
+ { args: ["--no-sandbox", "--disable-setuid-sandbox"] }
319
+ ) : {};
320
+ }
321
+ createBrowser(force) {
322
+ if (force || !this._browser) {
323
+ if (force && this._browser) {
324
+ this._browser.then((browser) => {
325
+ browser.isConnected() && browser.close();
326
+ });
327
+ }
328
+ this._browser = puppeteer.launch({
329
+ ...this.options,
330
+ headless: "new"
331
+ });
332
+ this._browser.then((browser) => {
333
+ this.browser = browser;
334
+ });
335
+ }
336
+ return this._browser;
337
+ }
338
+ async createPage(force) {
339
+ await this.createBrowser();
340
+ if (force || !this._page) {
341
+ if (force && this._page) {
342
+ this._page.then((page) => {
343
+ !page.isClosed() && page.close();
344
+ });
345
+ }
346
+ this._page = new Promise((resolve) => {
347
+ (async () => {
348
+ let page = await this.browser.newPage();
349
+ await page.evaluateOnNewDocument(
350
+ /* istanbul ignore next */
351
+ () => {
352
+ localStorage.clear();
353
+ }
354
+ );
355
+ page.on(
356
+ "console",
357
+ /* istanbul ignore next */
358
+ (e) => {
359
+ if (e.type() === "error") {
360
+ const err = e.args()[0];
361
+ console.error(
362
+ `Error from Puppeteer-loaded page:
363
+ `,
364
+ err.remoteObject().description
365
+ );
366
+ }
367
+ }
368
+ );
369
+ this.page = page;
370
+ this.operater = new Operater(page);
371
+ resolve(page);
372
+ })();
373
+ });
374
+ }
375
+ return this._page;
376
+ }
377
+ }
378
+
379
+ const TIME_OUT = 60 * 1e3;
380
+ const impl = () => {
381
+ let launch = new Launch();
382
+ beforeAll(async () => {
383
+ await launch.createBrowser();
384
+ }, 2e4);
385
+ beforeEach(async () => {
386
+ await launch.createPage(true);
387
+ });
388
+ afterEach(async () => {
389
+ if (!launch.page.isClosed()) {
390
+ await launch.page.close();
391
+ }
392
+ });
393
+ afterAll(async () => {
394
+ if (launch.browser.isConnected()) {
395
+ await launch.browser.close();
396
+ }
397
+ });
398
+ return launch;
399
+ };
400
+
401
+ const e2e = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
402
+ __proto__: null,
403
+ TIME_OUT,
404
+ impl
405
+ }, Symbol.toStringTag, { value: 'Module' }));
406
+
407
+ const sleep = (s) => new Promise((_) => {
408
+ setTimeout(_, s || 0);
409
+ });
410
+ const expectByPolling = async (poll, expected, options) => {
411
+ const { maxTries = 30, interval = 50, to } = options || {};
412
+ for (let tries = 0; tries < maxTries; tries++) {
413
+ const actual = await poll();
414
+ const allowMatch = (!to || to === "toMatch") && typeof expected === "string" && typeof actual === "string";
415
+ if (allowMatch && actual.indexOf(expected) > -1 || actual === expected || tries === maxTries - 1) {
416
+ allowMatch ? expect(actual).toMatch(expected) : expect(actual)[to || "toBe"](expected);
417
+ break;
418
+ } else {
419
+ await sleep(interval);
420
+ }
421
+ }
422
+ };
423
+ const def = (target, key, value, options) => {
424
+ Object.defineProperty(target, key, {
425
+ value,
426
+ enumerable: false,
427
+ writable: true,
428
+ configurable: true,
429
+ ...options
430
+ });
431
+ };
432
+
433
+ const utils = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
434
+ __proto__: null,
435
+ def,
436
+ expectByPolling,
437
+ sleep
438
+ }, Symbol.toStringTag, { value: 'Module' }));
439
+
138
440
  exports.Command = Command;
441
+ exports.E2E = e2e;
442
+ exports.Launch = Launch;
443
+ exports.Operater = Operater;
444
+ exports.Utils = utils;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  /// <reference types="node" />
2
2
 
3
+ import type { Browser } from 'puppeteer';
3
4
  import * as childProcess from 'child_process';
5
+ import type { ClickOptions } from 'puppeteer';
4
6
  import type { Nullable } from '@deot/dev-shared';
7
+ import type { Page } from 'puppeteer';
8
+ import type { PuppeteerLaunchOptions } from 'puppeteer';
5
9
 
6
10
  export declare class Command {
7
11
  target: Promise<any>;
@@ -24,4 +28,70 @@ export declare class Command {
24
28
  press(key: string, timeout?: number): Promise<void>;
25
29
  }
26
30
 
31
+ declare const def: (target: object, key: PropertyKey, value?: any, options?: PropertyDescriptor) => void;
32
+
33
+ declare namespace E2E {
34
+ export {
35
+ TIME_OUT,
36
+ impl
37
+ }
38
+ }
39
+ export { E2E }
40
+
41
+ declare const expectByPolling: (poll: () => Promise<any> | any, expected: any, options?: ExpectByPollingOptions) => Promise<void>;
42
+
43
+ declare interface ExpectByPollingOptions {
44
+ interval?: number;
45
+ maxTries?: number;
46
+ to?: string;
47
+ }
48
+
49
+ declare const impl: () => Launch;
50
+
51
+ export declare class Launch {
52
+ browser: Browser;
53
+ page: Page;
54
+ operater: Operater;
55
+ private _browser;
56
+ private _page;
57
+ options: PuppeteerLaunchOptions;
58
+ constructor();
59
+ createBrowser(force?: boolean): Promise<Browser>;
60
+ createPage(force?: boolean): Promise<Page>;
61
+ }
62
+
63
+ export declare class Operater {
64
+ page: Page;
65
+ constructor(v: Page);
66
+ click(selector: string, options?: ClickOptions): Promise<void>;
67
+ count(selector: string): Promise<number>;
68
+ text(selector: string): Promise<string>;
69
+ value(selector: string): Promise<string>;
70
+ html(selector: string): Promise<string>;
71
+ classList(selector: string): Promise<string[]>;
72
+ children(selector: string): Promise<HTMLElement[]>;
73
+ isVisible(selector: string): Promise<boolean>;
74
+ isChecked(selector: string): Promise<boolean>;
75
+ isFocused(selector: string): Promise<boolean>;
76
+ setValue(selector: string, value$: string): Promise<void>;
77
+ typeValue(selector: string, value$: string): Promise<void>;
78
+ enterValue(selector: string, value$: string): Promise<void>;
79
+ clearValue(selector: string): Promise<void>;
80
+ sleep(time: number): Promise<any>;
81
+ nextFrame(): Promise<unknown>;
82
+ }
83
+
84
+ declare const sleep: (s?: number) => Promise<unknown>;
85
+
86
+ declare const TIME_OUT: number;
87
+
88
+ declare namespace Utils {
89
+ export {
90
+ sleep,
91
+ expectByPolling,
92
+ def
93
+ }
94
+ }
95
+ export { Utils }
96
+
27
97
  export { }
package/dist/index.es.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Shell } from '@deot/dev-shared';
2
2
  import * as childProcess from 'child_process';
3
+ import puppeteer from 'puppeteer';
3
4
 
4
5
  const { LOCAL_COMMAND_MAP } = Shell;
5
6
  const KEY_MAP = {
@@ -112,4 +113,305 @@ class Command {
112
113
  }
113
114
  }
114
115
 
115
- export { Command };
116
+ class Operater {
117
+ page;
118
+ constructor(v) {
119
+ this.page = v;
120
+ }
121
+ /**
122
+ * @param {string} selector ~
123
+ * @param {ClickOptions} options ~
124
+ */
125
+ async click(selector, options) {
126
+ await this.page.click(selector, options);
127
+ }
128
+ /**
129
+ * @param {string} selector ~
130
+ * @returns {number} ~
131
+ */
132
+ async count(selector) {
133
+ return (await this.page.$$(selector)).length;
134
+ }
135
+ /**
136
+ * @param {string} selector ~
137
+ * @returns {Promise<string>} ~
138
+ */
139
+ async text(selector) {
140
+ return await this.page.$eval(selector, (node) => node.textContent);
141
+ }
142
+ /**
143
+ * @param {string} selector ~
144
+ * @returns {Promise<string>} ~
145
+ */
146
+ async value(selector) {
147
+ return await this.page.$eval(selector, (node) => node.value);
148
+ }
149
+ /**
150
+ * @param {string} selector ~
151
+ * @returns {Promise<string>} ~
152
+ */
153
+ async html(selector) {
154
+ return await this.page.$eval(selector, (node) => node.innerHTML);
155
+ }
156
+ /**
157
+ * @param {string} selector ~
158
+ * @returns {Promise<string[]>} ~
159
+ */
160
+ async classList(selector) {
161
+ return await this.page.$eval(selector, (node) => [...node.classList]);
162
+ }
163
+ /**
164
+ * @param {string} selector ~
165
+ * @returns {Promise<HTMLElement[]>} ~
166
+ */
167
+ async children(selector) {
168
+ return await this.page.$eval(selector, (node) => [...node.children]);
169
+ }
170
+ /**
171
+ * @param {string} selector ~
172
+ * @returns {Promise<boolean>} ~
173
+ */
174
+ async isVisible(selector) {
175
+ const display = await this.page.$eval(selector, (node) => {
176
+ return window.getComputedStyle(node).display;
177
+ });
178
+ return display !== "none";
179
+ }
180
+ /**
181
+ *
182
+ * @param {string} selector ~
183
+ * @returns {Promise<boolean>} ~
184
+ */
185
+ async isChecked(selector) {
186
+ return await this.page.$eval(
187
+ selector,
188
+ (node) => node.checked
189
+ );
190
+ }
191
+ /**
192
+ *
193
+ * @param {string} selector ~
194
+ * @returns {Promise<boolean>} ~
195
+ */
196
+ async isFocused(selector) {
197
+ return await this.page.$eval(selector, (node) => node === document.activeElement);
198
+ }
199
+ /**
200
+ * @param {string} selector ~
201
+ * @param {string} value$ ~
202
+ * @returns {Promise<void>} ~
203
+ */
204
+ async setValue(selector, value$) {
205
+ await this.page.$eval(
206
+ selector,
207
+ (node, value$$) => {
208
+ node.value = value$$;
209
+ node.dispatchEvent(new Event("input"));
210
+ },
211
+ value$
212
+ );
213
+ }
214
+ /**
215
+ * @param {string} selector ~
216
+ * @param {string} value$ ~
217
+ * @returns {Promise<void>} ~
218
+ */
219
+ async typeValue(selector, value$) {
220
+ const el = await this.page.$(selector);
221
+ await el.evaluate((node) => node.value = "");
222
+ await el.type(value$);
223
+ }
224
+ /**
225
+ * @param {string} selector ~
226
+ * @param {string} value$ ~
227
+ * @returns {Promise<void>} ~
228
+ */
229
+ async enterValue(selector, value$) {
230
+ const el = await this.page.$(selector);
231
+ await el.evaluate((node) => node.value = "");
232
+ await el.type(value$);
233
+ await el.press("Enter");
234
+ }
235
+ /**
236
+ * @param {string} selector ~
237
+ * @returns {Promise<void>} ~
238
+ */
239
+ async clearValue(selector) {
240
+ return await this.page.$eval(
241
+ selector,
242
+ (node) => {
243
+ node.value = "";
244
+ }
245
+ );
246
+ }
247
+ /**
248
+ *
249
+ * @param {number} time ~
250
+ * @returns {Promise<any>} ~
251
+ */
252
+ sleep(time) {
253
+ return this.page.evaluate((time$) => {
254
+ return new Promise((r) => {
255
+ setTimeout(r, time$);
256
+ });
257
+ }, time);
258
+ }
259
+ nextFrame() {
260
+ return this.page.evaluate(() => {
261
+ return new Promise((resolve) => {
262
+ requestAnimationFrame(() => {
263
+ requestAnimationFrame(resolve);
264
+ });
265
+ });
266
+ });
267
+ }
268
+ }
269
+
270
+ class Launch {
271
+ browser;
272
+ page;
273
+ operater;
274
+ _browser;
275
+ _page;
276
+ options;
277
+ constructor() {
278
+ this.operater = new Proxy({}, {
279
+ get: () => {
280
+ throw new Error("operater is not defined. create* invote first");
281
+ }
282
+ });
283
+ this.browser = new Proxy({}, {
284
+ get: () => {
285
+ throw new Error("browser is not defined. createBrowser invote first");
286
+ }
287
+ });
288
+ this.page = new Proxy({}, {
289
+ get: () => {
290
+ throw new Error("page is not defined. createPage invote first");
291
+ }
292
+ });
293
+ this.options = process.env.CI ? (
294
+ /* istanbul ignore next */
295
+ { args: ["--no-sandbox", "--disable-setuid-sandbox"] }
296
+ ) : {};
297
+ }
298
+ createBrowser(force) {
299
+ if (force || !this._browser) {
300
+ if (force && this._browser) {
301
+ this._browser.then((browser) => {
302
+ browser.isConnected() && browser.close();
303
+ });
304
+ }
305
+ this._browser = puppeteer.launch({
306
+ ...this.options,
307
+ headless: "new"
308
+ });
309
+ this._browser.then((browser) => {
310
+ this.browser = browser;
311
+ });
312
+ }
313
+ return this._browser;
314
+ }
315
+ async createPage(force) {
316
+ await this.createBrowser();
317
+ if (force || !this._page) {
318
+ if (force && this._page) {
319
+ this._page.then((page) => {
320
+ !page.isClosed() && page.close();
321
+ });
322
+ }
323
+ this._page = new Promise((resolve) => {
324
+ (async () => {
325
+ let page = await this.browser.newPage();
326
+ await page.evaluateOnNewDocument(
327
+ /* istanbul ignore next */
328
+ () => {
329
+ localStorage.clear();
330
+ }
331
+ );
332
+ page.on(
333
+ "console",
334
+ /* istanbul ignore next */
335
+ (e) => {
336
+ if (e.type() === "error") {
337
+ const err = e.args()[0];
338
+ console.error(
339
+ `Error from Puppeteer-loaded page:
340
+ `,
341
+ err.remoteObject().description
342
+ );
343
+ }
344
+ }
345
+ );
346
+ this.page = page;
347
+ this.operater = new Operater(page);
348
+ resolve(page);
349
+ })();
350
+ });
351
+ }
352
+ return this._page;
353
+ }
354
+ }
355
+
356
+ const TIME_OUT = 60 * 1e3;
357
+ const impl = () => {
358
+ let launch = new Launch();
359
+ beforeAll(async () => {
360
+ await launch.createBrowser();
361
+ }, 2e4);
362
+ beforeEach(async () => {
363
+ await launch.createPage(true);
364
+ });
365
+ afterEach(async () => {
366
+ if (!launch.page.isClosed()) {
367
+ await launch.page.close();
368
+ }
369
+ });
370
+ afterAll(async () => {
371
+ if (launch.browser.isConnected()) {
372
+ await launch.browser.close();
373
+ }
374
+ });
375
+ return launch;
376
+ };
377
+
378
+ const e2e = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
379
+ __proto__: null,
380
+ TIME_OUT,
381
+ impl
382
+ }, Symbol.toStringTag, { value: 'Module' }));
383
+
384
+ const sleep = (s) => new Promise((_) => {
385
+ setTimeout(_, s || 0);
386
+ });
387
+ const expectByPolling = async (poll, expected, options) => {
388
+ const { maxTries = 30, interval = 50, to } = options || {};
389
+ for (let tries = 0; tries < maxTries; tries++) {
390
+ const actual = await poll();
391
+ const allowMatch = (!to || to === "toMatch") && typeof expected === "string" && typeof actual === "string";
392
+ if (allowMatch && actual.indexOf(expected) > -1 || actual === expected || tries === maxTries - 1) {
393
+ allowMatch ? expect(actual).toMatch(expected) : expect(actual)[to || "toBe"](expected);
394
+ break;
395
+ } else {
396
+ await sleep(interval);
397
+ }
398
+ }
399
+ };
400
+ const def = (target, key, value, options) => {
401
+ Object.defineProperty(target, key, {
402
+ value,
403
+ enumerable: false,
404
+ writable: true,
405
+ configurable: true,
406
+ ...options
407
+ });
408
+ };
409
+
410
+ const utils = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
411
+ __proto__: null,
412
+ def,
413
+ expectByPolling,
414
+ sleep
415
+ }, Symbol.toStringTag, { value: 'Module' }));
416
+
417
+ export { Command, e2e as E2E, Launch, Operater, utils as Utils };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deot/dev-test",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "main": "dist/index.es.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -12,6 +12,7 @@
12
12
  "access": "public"
13
13
  },
14
14
  "dependencies": {
15
- "@deot/dev-shared": "^2.0.0"
15
+ "@deot/dev-shared": "^2.0.0",
16
+ "puppeteer": "^20.7.3"
16
17
  }
17
18
  }