@deot/dev-test 2.0.0 → 2.0.1
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 +289 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.es.js +286 -1
- package/package.json +3 -2
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,292 @@ 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 = this.browser.newPage();
|
|
347
|
+
this._page.then((page) => {
|
|
348
|
+
this.page = page;
|
|
349
|
+
this.operater = new Operater(page);
|
|
350
|
+
page.evaluateOnNewDocument(
|
|
351
|
+
/* istanbul ignore next */
|
|
352
|
+
() => {
|
|
353
|
+
localStorage.clear();
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
page.on(
|
|
357
|
+
"console",
|
|
358
|
+
/* istanbul ignore next */
|
|
359
|
+
(e) => {
|
|
360
|
+
if (e.type() === "error") {
|
|
361
|
+
const err = e.args()[0];
|
|
362
|
+
console.error(
|
|
363
|
+
`Error from Puppeteer-loaded page:
|
|
364
|
+
`,
|
|
365
|
+
err.remoteObject().description
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
return this._page;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const TIME_OUT = 30 * 1e3;
|
|
377
|
+
const impl = () => {
|
|
378
|
+
let launch = new Launch();
|
|
379
|
+
beforeAll(async () => {
|
|
380
|
+
await launch.createBrowser();
|
|
381
|
+
}, 2e4);
|
|
382
|
+
beforeEach(async () => {
|
|
383
|
+
await launch.createPage(true);
|
|
384
|
+
});
|
|
385
|
+
afterEach(async () => {
|
|
386
|
+
await launch.page.close();
|
|
387
|
+
});
|
|
388
|
+
afterAll(async () => {
|
|
389
|
+
await launch.browser.close();
|
|
390
|
+
});
|
|
391
|
+
return launch;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const e2e = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
395
|
+
__proto__: null,
|
|
396
|
+
TIME_OUT,
|
|
397
|
+
impl
|
|
398
|
+
}, Symbol.toStringTag, { value: 'Module' }));
|
|
399
|
+
|
|
400
|
+
const sleep = (s) => new Promise((_) => {
|
|
401
|
+
setTimeout(_, s || 0);
|
|
402
|
+
});
|
|
403
|
+
const expectByPolling = async (poll, expected, options) => {
|
|
404
|
+
const { maxTries = 30, interval = 50, to } = options || {};
|
|
405
|
+
for (let tries = 0; tries < maxTries; tries++) {
|
|
406
|
+
const actual = await poll();
|
|
407
|
+
const allowMatch = (!to || to === "toMatch") && typeof expected === "string" && typeof actual === "string";
|
|
408
|
+
if (allowMatch && actual.indexOf(expected) > -1 || actual === expected || tries === maxTries - 1) {
|
|
409
|
+
allowMatch ? expect(actual).toMatch(expected) : expect(actual)[to || "toBe"](expected);
|
|
410
|
+
break;
|
|
411
|
+
} else {
|
|
412
|
+
await sleep(interval);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const utils = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
418
|
+
__proto__: null,
|
|
419
|
+
expectByPolling,
|
|
420
|
+
sleep
|
|
421
|
+
}, Symbol.toStringTag, { value: 'Module' }));
|
|
422
|
+
|
|
138
423
|
exports.Command = Command;
|
|
424
|
+
exports.E2E = e2e;
|
|
425
|
+
exports.Launch = Launch;
|
|
426
|
+
exports.Operater = Operater;
|
|
427
|
+
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,67 @@ export declare class Command {
|
|
|
24
28
|
press(key: string, timeout?: number): Promise<void>;
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
declare namespace E2E {
|
|
32
|
+
export {
|
|
33
|
+
TIME_OUT,
|
|
34
|
+
impl
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export { E2E }
|
|
38
|
+
|
|
39
|
+
declare const expectByPolling: (poll: () => Promise<any> | any, expected: any, options?: ExpectByPollingOptions) => Promise<void>;
|
|
40
|
+
|
|
41
|
+
declare interface ExpectByPollingOptions {
|
|
42
|
+
interval?: number;
|
|
43
|
+
maxTries?: number;
|
|
44
|
+
to?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare const impl: () => Launch;
|
|
48
|
+
|
|
49
|
+
export declare class Launch {
|
|
50
|
+
browser: Browser;
|
|
51
|
+
page: Page;
|
|
52
|
+
operater: Operater;
|
|
53
|
+
private _browser;
|
|
54
|
+
private _page;
|
|
55
|
+
options: PuppeteerLaunchOptions;
|
|
56
|
+
constructor();
|
|
57
|
+
createBrowser(force?: boolean): Promise<Browser>;
|
|
58
|
+
createPage(force?: boolean): Promise<Page>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export declare class Operater {
|
|
62
|
+
page: Page;
|
|
63
|
+
constructor(v: Page);
|
|
64
|
+
click(selector: string, options?: ClickOptions): Promise<void>;
|
|
65
|
+
count(selector: string): Promise<number>;
|
|
66
|
+
text(selector: string): Promise<string>;
|
|
67
|
+
value(selector: string): Promise<string>;
|
|
68
|
+
html(selector: string): Promise<string>;
|
|
69
|
+
classList(selector: string): Promise<string[]>;
|
|
70
|
+
children(selector: string): Promise<HTMLElement[]>;
|
|
71
|
+
isVisible(selector: string): Promise<boolean>;
|
|
72
|
+
isChecked(selector: string): Promise<boolean>;
|
|
73
|
+
isFocused(selector: string): Promise<boolean>;
|
|
74
|
+
setValue(selector: string, value$: string): Promise<void>;
|
|
75
|
+
typeValue(selector: string, value$: string): Promise<void>;
|
|
76
|
+
enterValue(selector: string, value$: string): Promise<void>;
|
|
77
|
+
clearValue(selector: string): Promise<void>;
|
|
78
|
+
sleep(time: number): Promise<any>;
|
|
79
|
+
nextFrame(): Promise<unknown>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare const sleep: (s?: number) => Promise<unknown>;
|
|
83
|
+
|
|
84
|
+
declare const TIME_OUT: number;
|
|
85
|
+
|
|
86
|
+
declare namespace Utils {
|
|
87
|
+
export {
|
|
88
|
+
sleep,
|
|
89
|
+
expectByPolling
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export { Utils }
|
|
93
|
+
|
|
27
94
|
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,288 @@ class Command {
|
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
|
|
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 = this.browser.newPage();
|
|
324
|
+
this._page.then((page) => {
|
|
325
|
+
this.page = page;
|
|
326
|
+
this.operater = new Operater(page);
|
|
327
|
+
page.evaluateOnNewDocument(
|
|
328
|
+
/* istanbul ignore next */
|
|
329
|
+
() => {
|
|
330
|
+
localStorage.clear();
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
page.on(
|
|
334
|
+
"console",
|
|
335
|
+
/* istanbul ignore next */
|
|
336
|
+
(e) => {
|
|
337
|
+
if (e.type() === "error") {
|
|
338
|
+
const err = e.args()[0];
|
|
339
|
+
console.error(
|
|
340
|
+
`Error from Puppeteer-loaded page:
|
|
341
|
+
`,
|
|
342
|
+
err.remoteObject().description
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
return this._page;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const TIME_OUT = 30 * 1e3;
|
|
354
|
+
const impl = () => {
|
|
355
|
+
let launch = new Launch();
|
|
356
|
+
beforeAll(async () => {
|
|
357
|
+
await launch.createBrowser();
|
|
358
|
+
}, 2e4);
|
|
359
|
+
beforeEach(async () => {
|
|
360
|
+
await launch.createPage(true);
|
|
361
|
+
});
|
|
362
|
+
afterEach(async () => {
|
|
363
|
+
await launch.page.close();
|
|
364
|
+
});
|
|
365
|
+
afterAll(async () => {
|
|
366
|
+
await launch.browser.close();
|
|
367
|
+
});
|
|
368
|
+
return launch;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const e2e = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
372
|
+
__proto__: null,
|
|
373
|
+
TIME_OUT,
|
|
374
|
+
impl
|
|
375
|
+
}, Symbol.toStringTag, { value: 'Module' }));
|
|
376
|
+
|
|
377
|
+
const sleep = (s) => new Promise((_) => {
|
|
378
|
+
setTimeout(_, s || 0);
|
|
379
|
+
});
|
|
380
|
+
const expectByPolling = async (poll, expected, options) => {
|
|
381
|
+
const { maxTries = 30, interval = 50, to } = options || {};
|
|
382
|
+
for (let tries = 0; tries < maxTries; tries++) {
|
|
383
|
+
const actual = await poll();
|
|
384
|
+
const allowMatch = (!to || to === "toMatch") && typeof expected === "string" && typeof actual === "string";
|
|
385
|
+
if (allowMatch && actual.indexOf(expected) > -1 || actual === expected || tries === maxTries - 1) {
|
|
386
|
+
allowMatch ? expect(actual).toMatch(expected) : expect(actual)[to || "toBe"](expected);
|
|
387
|
+
break;
|
|
388
|
+
} else {
|
|
389
|
+
await sleep(interval);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const utils = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
395
|
+
__proto__: null,
|
|
396
|
+
expectByPolling,
|
|
397
|
+
sleep
|
|
398
|
+
}, Symbol.toStringTag, { value: 'Module' }));
|
|
399
|
+
|
|
400
|
+
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.
|
|
3
|
+
"version": "2.0.1",
|
|
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
|
}
|