mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 17:58:43 -05:00
chore: expand rendering test capabilities (#1011)
* test: implement rendering tests with listbox * test: keep headless * chore: revert changing puppeteer version * fix: clean up
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,6 +14,7 @@ dist/
|
||||
temp/
|
||||
test/**/__artifacts__/regression
|
||||
test/**/__artifacts__/diff
|
||||
test/**/__artifacts__/temp
|
||||
apis/*/core/**/*.js
|
||||
apis/*/core/**/*.js.map
|
||||
apis/locale/all.json
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"test:watch": "jest --watch",
|
||||
"test:coverage": "jest --coverage",
|
||||
"test:mashup": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/mashup/**/*.int.js'",
|
||||
"test:rendering": "node ./scripts/run-rendering-tests.js",
|
||||
"test:integration": "aw puppet -c aw.config.js --testExt '*.int.js' --glob 'test/integration/**/*.int.js'",
|
||||
"test:component": "aw puppet -c aw.config.js --testExt '*.comp.js' --glob 'test/component/**/*.comp.js'",
|
||||
"prepare": "husky install"
|
||||
@@ -91,6 +92,7 @@
|
||||
"picasso.js": "1.9.6",
|
||||
"prettier": "2.7.1",
|
||||
"pretty-quick": "3.1.3",
|
||||
"puppeteer": "19.2.2",
|
||||
"qix-faker": "0.3.0",
|
||||
"rollup": "3.2.5",
|
||||
"rollup-plugin-babel": "4.4.0",
|
||||
|
||||
4
scripts/run-rendering-tests.js
Normal file
4
scripts/run-rendering-tests.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const cmd = 'mocha test/rendering/listbox/listbox.spec.js --bail false --timeout 30000';
|
||||
execSync(cmd, { stdio: 'inherit' });
|
||||
28
test/rendering/README.md
Normal file
28
test/rendering/README.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Rendering tests
|
||||
|
||||
These tests are aimed for more generic rendering tests than the **mashup** and **integration** counterparts. These tests do not rely on a Supernova or custom backend solution for producing screenshots. If you want to test a Supernova based viz, you should instead create either a [mashup](../mashup) or an [integration](../integration) test.
|
||||
|
||||
## Tests covered (so far)
|
||||
|
||||
- [Listboxes](./listbox)
|
||||
|
||||
## Create and run tests
|
||||
|
||||
1. Create a folder, e.g. `./test/rendering/listbox`
|
||||
2. Inside that folder, create:
|
||||
- listbox.spec.js - a test file which orchestrates interactions and when to take a screenshot
|
||||
- listbox.html - constitutes the "site" that the test file interacts with and creates screenshots from
|
||||
- listbox.js - (optional) if you don't want to add all JS code inside of the html file
|
||||
3. Run tests from the nebula.js root with `yarn test:rendering`
|
||||
4. The first time, the test will fail and create an image inside of the `__artifacts__/temp` folder. Drag this file to the `__artifacts__/baseline` folder and re-run the test to verify that it passes. Then `git push` the baseline.
|
||||
|
||||
Check the [listbox files](./listbox) for details on how to write the code in these files.
|
||||
|
||||
## Output folders
|
||||
|
||||
Screenshots are stored in the following folders:
|
||||
|
||||
- `temp` - new screenshots are stored here
|
||||
- `baseline` - new screenshots are compared to the baseline version of the image, in order to check validity – only update these if a change is expected
|
||||
- `regression` - new screenshots are saved here when deviating too much (i.e. when they are invalid)
|
||||
- `diff` - the difference between the baseline and the regression image
|
||||
BIN
test/rendering/__artifacts__/baseline/listbox_select_EH.png
Normal file
BIN
test/rendering/__artifacts__/baseline/listbox_select_EH.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
239
test/rendering/listbox/listbox-data.js
Normal file
239
test/rendering/listbox/listbox-data.js
Normal file
@@ -0,0 +1,239 @@
|
||||
window.getFuncs = function getFuncs() {
|
||||
return {
|
||||
getMockData: () => [
|
||||
{
|
||||
qMatrix: [
|
||||
[
|
||||
{
|
||||
qText: 'A',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 0,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'B',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 1,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'C',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 2,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'D',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 3,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'E',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 4,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'F',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 5,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'G',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 6,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'H',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 7,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'I',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 8,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'J',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 9,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'K',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 10,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'L',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 11,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
],
|
||||
qTails: [],
|
||||
qArea: {
|
||||
qLeft: 0,
|
||||
qTop: 0,
|
||||
qWidth: 1,
|
||||
qHeight: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
qMatrix: [
|
||||
[
|
||||
{
|
||||
qText: 'A',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 0,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'B',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 1,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'C',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 2,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'D',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 3,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'E',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 4,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'F',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 5,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'G',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 6,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'H',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 7,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'I',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 8,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'J',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 9,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'K',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 10,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
qText: 'L',
|
||||
qNum: 'NaN',
|
||||
qElemNumber: 11,
|
||||
qState: 'O',
|
||||
},
|
||||
],
|
||||
],
|
||||
qTails: [],
|
||||
qArea: {
|
||||
qLeft: 0,
|
||||
qTop: 0,
|
||||
qWidth: 1,
|
||||
qHeight: 12,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
getListboxLayout: () => ({
|
||||
qInfo: {
|
||||
qId: 'qId',
|
||||
},
|
||||
visualization: 'listbox',
|
||||
qListObject: {
|
||||
qDimensionInfo: {
|
||||
qLocked: false,
|
||||
},
|
||||
qSize: {
|
||||
qcy: 12,
|
||||
},
|
||||
qInitialDataFetch: [{ qLeft: 0, qWidth: 0, qTop: 0, qHeight: 0 }],
|
||||
},
|
||||
qSelectionInfo: {
|
||||
qInSelections: false,
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
27
test/rendering/listbox/listbox.html
Normal file
27
test/rendering/listbox/listbox.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script src="/apis/stardust/dist/stardust.js"></script>
|
||||
<script src="./listbox-data.js"></script>
|
||||
<script defer src="./listbox.js"></script>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#object {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="object" data-type="listbox"></div>
|
||||
</body>
|
||||
</html>
|
||||
40
test/rendering/listbox/listbox.js
Normal file
40
test/rendering/listbox/listbox.js
Normal file
@@ -0,0 +1,40 @@
|
||||
(() => {
|
||||
function getMocks() {
|
||||
const { getMockData, getListboxLayout } = window.getFuncs();
|
||||
const obj = {
|
||||
id: `listbox-${+new Date()}`,
|
||||
getListObjectData: async () => getMockData(),
|
||||
getLayout: async () => getListboxLayout(),
|
||||
on() {},
|
||||
once() {},
|
||||
};
|
||||
|
||||
const app = {
|
||||
id: `${+new Date()}`,
|
||||
session: {},
|
||||
createSessionObject: async () => obj,
|
||||
getObject: async () => obj,
|
||||
getAppLayout: async () => ({ qTitle: '', qLocaleInfo: {} }),
|
||||
};
|
||||
return {
|
||||
obj,
|
||||
app,
|
||||
};
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
const element = window.document.querySelector('#object');
|
||||
const { app } = getMocks();
|
||||
const nebbie = window.stardust.embed(app);
|
||||
const listboxOptions = {
|
||||
dense: false,
|
||||
};
|
||||
const inst = await nebbie.field('Alpha');
|
||||
inst.mount(element, listboxOptions);
|
||||
return () => {
|
||||
inst?.unmount(element);
|
||||
};
|
||||
};
|
||||
|
||||
return init();
|
||||
})();
|
||||
42
test/rendering/listbox/listbox.spec.js
Normal file
42
test/rendering/listbox/listbox.spec.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const getPage = require('../setup');
|
||||
const startServer = require('../server');
|
||||
const { execSequence, looksLike } = require('../testUtils');
|
||||
|
||||
describe('listbox mashup rendering test', () => {
|
||||
const object = '[data-type="listbox"]';
|
||||
const listboxSelector = `${object} .listbox-container`;
|
||||
let page;
|
||||
let takeScreenshot;
|
||||
let destroyServer;
|
||||
let destroyBrowser;
|
||||
let url;
|
||||
const PAGE_OPTIONS = { width: 300, height: 500 };
|
||||
|
||||
beforeEach(async () => {
|
||||
({ url, destroy: destroyServer } = await startServer());
|
||||
({ page, takeScreenshot, destroy: destroyBrowser } = await getPage(PAGE_OPTIONS));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all([destroyServer(), destroyBrowser()]);
|
||||
});
|
||||
|
||||
it('selecting two values should result in two green rows', async () => {
|
||||
const FILE_NAME = 'listbox_select_EH.png';
|
||||
|
||||
await page.goto(`${url}/listbox/listbox.html`);
|
||||
await page.waitForSelector(listboxSelector, { visible: true });
|
||||
|
||||
const selectNumbers = [4, 7];
|
||||
const action = async (nbr) => {
|
||||
const rowSelector = `${listboxSelector} [data-n="${nbr}"]`;
|
||||
await page.click(rowSelector);
|
||||
};
|
||||
|
||||
await execSequence(selectNumbers, action);
|
||||
|
||||
const snapshotElement = await page.$(listboxSelector);
|
||||
const { path: capturedPath } = await takeScreenshot(FILE_NAME, snapshotElement);
|
||||
await looksLike(FILE_NAME, capturedPath);
|
||||
});
|
||||
});
|
||||
31
test/rendering/server.js
Normal file
31
test/rendering/server.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
|
||||
async function startServer() {
|
||||
const app = express();
|
||||
const port = 8050;
|
||||
const url = `http://localhost:${port}`;
|
||||
app.use(express.static(path.resolve(__dirname)));
|
||||
app.use('/apis', express.static(path.resolve(__dirname, '../../apis')));
|
||||
app.use('/node_modules', express.static(path.resolve(__dirname, '../../node_modules')));
|
||||
|
||||
let server;
|
||||
|
||||
await new Promise((resolve) => {
|
||||
server = app.listen(port, () => {
|
||||
console.log(`Running rendering server at ${url}`);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const destroy = () => {
|
||||
server.close();
|
||||
};
|
||||
|
||||
return {
|
||||
url,
|
||||
destroy,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = startServer;
|
||||
38
test/rendering/setup.js
Normal file
38
test/rendering/setup.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const path = require('path');
|
||||
|
||||
const artifactsPath = path.join(__dirname, './__artifacts__');
|
||||
|
||||
async function getPage(options = {}) {
|
||||
const { width, height } = options;
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
// Uncomment these for debugging the test visually.
|
||||
// headless: false,
|
||||
// slowMo: 200,
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width, height });
|
||||
page.setDefaultNavigationTimeout(30000);
|
||||
page.setDefaultTimeout(30000);
|
||||
|
||||
const destroy = async () => {
|
||||
await browser.close();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} fileName The name of the output file, should match the baseline version's name.
|
||||
* @param {HTMLElement} elm If you want to restrict the screenshot area to a certain element.
|
||||
* @returns {Promise} Resolves the absolute path to the screenshot.
|
||||
*/
|
||||
const takeScreenshot = async (fileName, elm = undefined) => {
|
||||
const screenshotPath = path.resolve(artifactsPath, './temp', fileName);
|
||||
const clip = await elm?.boundingBox();
|
||||
await page.screenshot({ clip, path: screenshotPath });
|
||||
return { path: screenshotPath };
|
||||
};
|
||||
return { browser, page, destroy, takeScreenshot };
|
||||
}
|
||||
|
||||
module.exports = getPage;
|
||||
41
test/rendering/testUtils.js
Normal file
41
test/rendering/testUtils.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const path = require('path');
|
||||
const jimp = require('jimp');
|
||||
|
||||
async function looksLike(fileName, capturedPath) {
|
||||
const artifactsPath = path.resolve(__dirname, './__artifacts__/');
|
||||
const storedPath = path.resolve(artifactsPath, 'baseline', fileName);
|
||||
|
||||
const stored = await jimp.read(storedPath);
|
||||
const captured = await jimp.read(capturedPath);
|
||||
|
||||
const distance = jimp.distance(stored, captured);
|
||||
const diff = jimp.diff(stored, captured);
|
||||
|
||||
if (distance > 0.001 || diff.percent > 0.007) {
|
||||
await captured.writeAsync(path.resolve(artifactsPath, 'regression', fileName));
|
||||
await diff.image.writeAsync(path.resolve(artifactsPath, 'diff', fileName));
|
||||
throw new Error(`Images differ too much - distance: ${distance}, percent: ${diff.percent}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function for ensuring that each action is awaited before executing the next one.
|
||||
* @param {function[]} items An array of items (e.g. selectors) that will be sent into the action function, iteratively.
|
||||
* @returns {Promise} Resolves true when done.
|
||||
*/
|
||||
async function execSequence(items, action) {
|
||||
const takeAction = async (index = 0) => {
|
||||
if (index >= items.length) {
|
||||
return true; // done
|
||||
}
|
||||
const nextItem = items[index];
|
||||
await action(nextItem);
|
||||
return takeAction(index + 1);
|
||||
};
|
||||
return takeAction();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
execSequence,
|
||||
looksLike,
|
||||
};
|
||||
55
yarn.lock
55
yarn.lock
@@ -9552,6 +9552,17 @@ cosmiconfig-typescript-loader@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.0.0.tgz#4a6d856c1281135197346a6f64dfa73a9cd9fefa"
|
||||
integrity sha512-cVpucSc2Tf+VPwCCR7SZzmQTQkPbkk4O01yXsYqXBIbjE1bhwqSyAgYQkRK1un4i0OPziTleqFhdkmOc4RQ/9g==
|
||||
|
||||
cosmiconfig@7.0.1, cosmiconfig@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d"
|
||||
integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
|
||||
dependencies:
|
||||
"@types/parse-json" "^4.0.0"
|
||||
import-fresh "^3.2.1"
|
||||
parse-json "^5.0.0"
|
||||
path-type "^4.0.0"
|
||||
yaml "^1.10.0"
|
||||
|
||||
cosmiconfig@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
|
||||
@@ -9563,17 +9574,6 @@ cosmiconfig@^6.0.0:
|
||||
path-type "^4.0.0"
|
||||
yaml "^1.7.2"
|
||||
|
||||
cosmiconfig@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d"
|
||||
integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
|
||||
dependencies:
|
||||
"@types/parse-json" "^4.0.0"
|
||||
import-fresh "^3.2.1"
|
||||
parse-json "^5.0.0"
|
||||
path-type "^4.0.0"
|
||||
yaml "^1.10.0"
|
||||
|
||||
cp-file@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-6.2.0.tgz#40d5ea4a1def2a9acdd07ba5c0b0246ef73dc10d"
|
||||
@@ -10245,6 +10245,11 @@ devtools-protocol@0.0.1001819:
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1001819.tgz#0a98f44cefdb02cc684f3d5e6bd898a1690231d9"
|
||||
integrity sha512-G6OsIFnv/rDyxSqBa2lDLR6thp9oJioLsb2Gl+LbQlyoA9/OBAkrTU9jiCcQ8Pnh7z4d6slDiLaogR5hzgJLmQ==
|
||||
|
||||
devtools-protocol@0.0.1056733:
|
||||
version "0.0.1056733"
|
||||
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1056733.tgz#55bb1d56761014cc221131cca5e6bad94eefb2b9"
|
||||
integrity sha512-CmTu6SQx2g3TbZzDCAV58+LTxVdKplS7xip0g5oDXpZ+isr0rv5dDP8ToyVRywzPHkCCPKgKgScEcwz4uPWDIA==
|
||||
|
||||
dezalgo@^1.0.0:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81"
|
||||
@@ -18039,6 +18044,22 @@ puppeteer-core@1.20.0:
|
||||
rimraf "^2.6.1"
|
||||
ws "^6.1.0"
|
||||
|
||||
puppeteer-core@19.2.2:
|
||||
version "19.2.2"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-19.2.2.tgz#a9b7b25099d87d550c224c660c205b7ebdd03eb4"
|
||||
integrity sha512-faojf+1pZ/tHXSr4x1q+9MVd9FrL3rpdbC0w7qN7MNClMoLuCvMbpR4vzcjoiJYgclt1n+SOPUOmHQViTw6frw==
|
||||
dependencies:
|
||||
cross-fetch "3.1.5"
|
||||
debug "4.3.4"
|
||||
devtools-protocol "0.0.1056733"
|
||||
extract-zip "2.0.1"
|
||||
https-proxy-agent "5.0.1"
|
||||
proxy-from-env "1.1.0"
|
||||
rimraf "3.0.2"
|
||||
tar-fs "2.1.1"
|
||||
unbzip2-stream "1.4.3"
|
||||
ws "8.10.0"
|
||||
|
||||
puppeteer@14.4.1:
|
||||
version "14.4.1"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-14.4.1.tgz#6c7437a65f7ba98ef8ad7c2b0f1cf808e91617bb"
|
||||
@@ -18057,6 +18078,18 @@ puppeteer@14.4.1:
|
||||
unbzip2-stream "1.4.3"
|
||||
ws "8.7.0"
|
||||
|
||||
puppeteer@19.2.2:
|
||||
version "19.2.2"
|
||||
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-19.2.2.tgz#e6f7bc089ac9bffea78b2f792bf3affd93e16803"
|
||||
integrity sha512-m1T5Mog5qu5+dMBptWYTn6pXRdnFbydbVUCthqwbfd8/kOiMlzZBR9ywjX79LpvI1Sj+/z8+FKeIsjnMul8ZYA==
|
||||
dependencies:
|
||||
cosmiconfig "7.0.1"
|
||||
devtools-protocol "0.0.1056733"
|
||||
https-proxy-agent "5.0.1"
|
||||
progress "2.0.3"
|
||||
proxy-from-env "1.1.0"
|
||||
puppeteer-core "19.2.2"
|
||||
|
||||
q@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
|
||||
|
||||
Reference in New Issue
Block a user