@uuv/playwright 2.0.2 → 2.1.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/dist/cucumber/preprocessor/gen/generate.d.ts +3 -1
- package/dist/cucumber/preprocessor/gen/generate.js +22 -2
- package/dist/cucumber/preprocessor/gen/index.d.ts +1 -0
- package/dist/cucumber/preprocessor/gen/index.js +4 -4
- package/dist/lib/runner-playwright.d.ts +2 -2
- package/dist/lib/runner-playwright.js +29 -11
- package/dist/lib/uuv-cli.js +2 -22
- package/dist/lib/watch-test-files.js +4 -3
- package/dist/reporter/uuv-playwright-reporter-helper.d.ts +1 -0
- package/dist/reporter/uuv-playwright-reporter-helper.js +37 -2
- package/dist/reporter/uuv-playwright-reporter.js +2 -1
- package/package.json +5 -4
|
@@ -5,9 +5,11 @@ import { GherkinDocument, Pickle } from "@cucumber/messages";
|
|
|
5
5
|
export declare class PWFile {
|
|
6
6
|
doc: GherkinDocument;
|
|
7
7
|
private pickles;
|
|
8
|
+
private readonly tags?;
|
|
8
9
|
private lines;
|
|
9
10
|
private keywordsMap?;
|
|
10
|
-
|
|
11
|
+
private tagsExpression;
|
|
12
|
+
constructor(doc: GherkinDocument, pickles: Pickle[], tags?: string | undefined);
|
|
11
13
|
get content(): string;
|
|
12
14
|
get language(): string;
|
|
13
15
|
build(): this;
|
|
@@ -22,18 +22,28 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
29
|
exports.PWFile = void 0;
|
|
27
30
|
const formatter = __importStar(require("./formatter"));
|
|
28
31
|
const i18n_1 = require("./i18n");
|
|
32
|
+
const tag_expressions_1 = __importDefault(require("@cucumber/tag-expressions"));
|
|
29
33
|
class PWFile {
|
|
30
34
|
doc;
|
|
31
35
|
pickles;
|
|
36
|
+
tags;
|
|
32
37
|
lines = [];
|
|
33
38
|
keywordsMap;
|
|
34
|
-
|
|
39
|
+
tagsExpression;
|
|
40
|
+
constructor(doc, pickles, tags) {
|
|
35
41
|
this.doc = doc;
|
|
36
42
|
this.pickles = pickles;
|
|
43
|
+
this.tags = tags;
|
|
44
|
+
if (tags) {
|
|
45
|
+
this.tagsExpression = (0, tag_expressions_1.default)(tags);
|
|
46
|
+
}
|
|
37
47
|
}
|
|
38
48
|
get content() {
|
|
39
49
|
return this.lines.join("\n");
|
|
@@ -71,7 +81,17 @@ class PWFile {
|
|
|
71
81
|
return this.getBeforeEach(background);
|
|
72
82
|
}
|
|
73
83
|
if (scenario) {
|
|
74
|
-
|
|
84
|
+
if (this.tagsExpression) {
|
|
85
|
+
if (this.tagsExpression.evaluate(scenario.tags.map(tag => tag.name))) {
|
|
86
|
+
return this.getScenarioLines(scenario);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return this.getScenarioLines(scenario);
|
|
94
|
+
}
|
|
75
95
|
}
|
|
76
96
|
throw new Error(`Empty child: ${JSON.stringify(child)}`);
|
|
77
97
|
}
|
|
@@ -17,9 +17,9 @@ const defaults = {
|
|
|
17
17
|
cwd: process.cwd(),
|
|
18
18
|
};
|
|
19
19
|
async function generateTestFiles(inputOptions) {
|
|
20
|
-
const { outputDir, cwd, cucumberConfig } = Object.assign({}, defaults, inputOptions);
|
|
20
|
+
const { outputDir, cwd, cucumberConfig, tags } = Object.assign({}, defaults, inputOptions);
|
|
21
21
|
const features = await loadFeatures({ file: cucumberConfig }, { cwd });
|
|
22
|
-
const files = buildFiles(features);
|
|
22
|
+
const files = buildFiles(features, tags);
|
|
23
23
|
const paths = (0, save_1.saveFiles)(files, node_path_1.default.join(cwd, outputDir));
|
|
24
24
|
const mapOfFile = new Map();
|
|
25
25
|
paths.forEach((path, index) => mapOfFile.set(path, Array.from(features.keys())[index]));
|
|
@@ -32,9 +32,9 @@ async function loadFeatures(options, environment) {
|
|
|
32
32
|
handleParseErrors(parseErrors);
|
|
33
33
|
return groupByDocument(filteredPickles);
|
|
34
34
|
}
|
|
35
|
-
function buildFiles(features) {
|
|
35
|
+
function buildFiles(features, tags) {
|
|
36
36
|
const files = [];
|
|
37
|
-
features.forEach((pickles, doc) => files.push(new generate_1.PWFile(doc, pickles).build()));
|
|
37
|
+
features.forEach((pickles, doc) => files.push(new generate_1.PWFile(doc, pickles, tags).build()));
|
|
38
38
|
return files;
|
|
39
39
|
}
|
|
40
40
|
function groupByDocument(filteredPickles) {
|
|
@@ -17,5 +17,5 @@ export interface UUVPlaywrightCucumberMapItem {
|
|
|
17
17
|
generatedFile: string;
|
|
18
18
|
}
|
|
19
19
|
export declare const UUVPlaywrightCucumberMapFile = ".uuv-playwright-cucumber-map.json";
|
|
20
|
-
export declare function executePreprocessor(tempDir: string, configDir: string): Promise<void>;
|
|
21
|
-
export declare function run(mode: "open" | "e2e", tempDir
|
|
20
|
+
export declare function executePreprocessor(tempDir: string, configDir: string, env: any): Promise<void>;
|
|
21
|
+
export declare function run(mode: "open" | "e2e", tempDir: string | undefined, configDir: string | undefined, argv: any): Promise<void>;
|
|
@@ -48,10 +48,11 @@ const child_process_1 = __importStar(require("child_process"));
|
|
|
48
48
|
const uuv_playwright_reporter_helper_1 = require("../reporter/uuv-playwright-reporter-helper");
|
|
49
49
|
const path_1 = __importDefault(require("path"));
|
|
50
50
|
exports.UUVPlaywrightCucumberMapFile = ".uuv-playwright-cucumber-map.json";
|
|
51
|
-
async function bddGen(tempDir) {
|
|
51
|
+
async function bddGen(tempDir, env) {
|
|
52
52
|
try {
|
|
53
53
|
const mapOfFile = await (0, gen_1.generateTestFiles)({
|
|
54
54
|
outputDir: tempDir,
|
|
55
|
+
tags: env.TAGS
|
|
55
56
|
});
|
|
56
57
|
const content = [];
|
|
57
58
|
mapOfFile.forEach((value, key) => {
|
|
@@ -112,7 +113,7 @@ function translateFeatures(tempDir, configDir) {
|
|
|
112
113
|
console.log(chalk_1.default.gray(`[WRITE] ${generatedFile} written successfully`));
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
|
-
function runPlaywright(mode, configDir, generateHtmlReport = false, env, targetTestFile) {
|
|
116
|
+
function runPlaywright(mode, configDir, browser = "chromium", generateHtmlReport = false, env, targetTestFile) {
|
|
116
117
|
const configFile = `${configDir}/playwright.config.ts`;
|
|
117
118
|
const reportType = generateHtmlReport ? uuv_playwright_reporter_helper_1.GeneratedReportType.HTML : uuv_playwright_reporter_helper_1.GeneratedReportType.CONSOLE;
|
|
118
119
|
try {
|
|
@@ -122,10 +123,14 @@ function runPlaywright(mode, configDir, generateHtmlReport = false, env, targetT
|
|
|
122
123
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
123
124
|
// @ts-ignore
|
|
124
125
|
process.env.CONFIG_DIR = configDir;
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
127
|
+
// @ts-ignore
|
|
128
|
+
process.env.browser = browser;
|
|
125
129
|
if (env) {
|
|
126
130
|
Object.keys(env).forEach(key => process.env[key] = env[key]);
|
|
127
131
|
}
|
|
128
|
-
|
|
132
|
+
// eslint-disable-next-line max-len
|
|
133
|
+
const command = `npx playwright test --project=${browser} -c ${configFile} ${mode === "open" ? "--ui" : ""}${getTargetTestFileForPlawright(targetTestFile)}`;
|
|
129
134
|
console.log(chalk_1.default.gray(`Running ${command}`));
|
|
130
135
|
(0, child_process_1.execSync)(command, { stdio: "inherit" });
|
|
131
136
|
}
|
|
@@ -137,22 +142,35 @@ function getTargetTestFileForPlawright(targetTestFile) {
|
|
|
137
142
|
if (!targetTestFile) {
|
|
138
143
|
return "";
|
|
139
144
|
}
|
|
140
|
-
return targetTestFile
|
|
145
|
+
return ` ${targetTestFile
|
|
141
146
|
.replaceAll("uuv/e2e/", ".uuv-features-gen/uuv/e2e/")
|
|
142
|
-
.replaceAll(".feature", ".feature.spec.js")
|
|
147
|
+
.replaceAll(".feature", ".feature.spec.js")}`;
|
|
143
148
|
}
|
|
144
|
-
async function executePreprocessor(tempDir, configDir) {
|
|
149
|
+
async function executePreprocessor(tempDir, configDir, env) {
|
|
145
150
|
console.log("running preprocessor...");
|
|
146
|
-
await bddGen(tempDir);
|
|
151
|
+
await bddGen(tempDir, env);
|
|
147
152
|
translateFeatures(tempDir, configDir);
|
|
148
153
|
console.log("preprocessor executed");
|
|
149
154
|
}
|
|
150
155
|
exports.executePreprocessor = executePreprocessor;
|
|
151
|
-
async function run(mode, tempDir = "uuv/.features-gen/e2e", configDir = "uuv",
|
|
152
|
-
|
|
156
|
+
async function run(mode, tempDir = "uuv/.features-gen/e2e", configDir = "uuv", argv) {
|
|
157
|
+
const { browser, env, targetTestFile } = extractArgs(argv);
|
|
158
|
+
await executePreprocessor(tempDir, configDir, env);
|
|
153
159
|
if (mode === "open") {
|
|
154
|
-
child_process_1.default.fork(path_1.default.join(__dirname, "watch-test-files"), [tempDir, configDir]);
|
|
160
|
+
child_process_1.default.fork(path_1.default.join(__dirname, "watch-test-files"), [tempDir, configDir, env]);
|
|
155
161
|
}
|
|
156
|
-
runPlaywright(mode, configDir, generateHtmlReport, env, targetTestFile);
|
|
162
|
+
runPlaywright(mode, configDir, browser, argv.generateHtmlReport, env, targetTestFile);
|
|
157
163
|
}
|
|
158
164
|
exports.run = run;
|
|
165
|
+
function extractArgs(argv) {
|
|
166
|
+
const browser = argv.browser;
|
|
167
|
+
const env = argv.env ? JSON.parse(argv.env.replace(/'/g, "\"")) : {};
|
|
168
|
+
const targetTestFile = argv.targetTestFile ? argv.targetTestFile : null;
|
|
169
|
+
console.debug("Variables: ");
|
|
170
|
+
console.debug(` -> browser: ${browser}`);
|
|
171
|
+
console.debug(` -> env: ${JSON.stringify(env)}`);
|
|
172
|
+
if (targetTestFile) {
|
|
173
|
+
console.debug(` -> targetTestFile: ${targetTestFile}`);
|
|
174
|
+
}
|
|
175
|
+
return { browser, env, targetTestFile };
|
|
176
|
+
}
|
package/dist/lib/uuv-cli.js
CHANGED
|
@@ -43,32 +43,12 @@ async function main() {
|
|
|
43
43
|
process.exit(1);
|
|
44
44
|
}
|
|
45
45
|
console.info(`UUV command ${command} executed`);
|
|
46
|
-
function extractArgs(argv) {
|
|
47
|
-
const browser = argv.browser ? argv.browser : "chrome";
|
|
48
|
-
const env = argv.env ? JSON.parse(argv.env.replace(/'/g, "\"")) : {};
|
|
49
|
-
const targetTestFile = argv.targetTestFile ? argv.targetTestFile : null;
|
|
50
|
-
console.debug("Variables: ");
|
|
51
|
-
console.debug(` -> browser: ${browser}`);
|
|
52
|
-
console.debug(` -> env: ${JSON.stringify(env)}`);
|
|
53
|
-
if (targetTestFile) {
|
|
54
|
-
console.debug(` -> targetTestFile: ${targetTestFile}`);
|
|
55
|
-
}
|
|
56
|
-
return { browser, env, targetTestFile };
|
|
57
|
-
}
|
|
58
46
|
function openPlaywright(argv) {
|
|
59
|
-
|
|
60
|
-
//const { env } = extractArgs(argv);
|
|
61
|
-
return (0, runner_playwright_1.run)("open", FEATURE_GEN_DIR, PROJECT_DIR);
|
|
47
|
+
return (0, runner_playwright_1.run)("open", FEATURE_GEN_DIR, PROJECT_DIR, argv);
|
|
62
48
|
}
|
|
63
49
|
function runE2ETests(argv) {
|
|
64
|
-
const { browser, env, targetTestFile } = extractArgs(argv);
|
|
65
|
-
// TODO Manage HTML Report
|
|
66
|
-
// Creating needed dirs
|
|
67
|
-
// if (!fs.existsSync(JSON_REPORT_DIR)) {
|
|
68
|
-
// fs.mkdirSync(JSON_REPORT_DIR, { recursive: true });
|
|
69
|
-
// }
|
|
70
50
|
// Running Tests
|
|
71
|
-
return (0, runner_playwright_1.run)("e2e", FEATURE_GEN_DIR, PROJECT_DIR, argv
|
|
51
|
+
return (0, runner_playwright_1.run)("e2e", FEATURE_GEN_DIR, PROJECT_DIR, argv)
|
|
72
52
|
.then(async (result) => {
|
|
73
53
|
console.log(`Status ${chalk_1.default.green("success")}`);
|
|
74
54
|
})
|
|
@@ -22,6 +22,7 @@ const chokidar_1 = __importDefault(require("chokidar"));
|
|
|
22
22
|
const chalk_1 = __importDefault(require("chalk"));
|
|
23
23
|
const tempDir = process.argv[2];
|
|
24
24
|
const configDir = process.argv[3];
|
|
25
|
+
const env = process.argv[4];
|
|
25
26
|
if (!tempDir || !configDir) {
|
|
26
27
|
console.log(chalk_1.default.redBright("An error occurred during test files watching"));
|
|
27
28
|
process.exit(-1);
|
|
@@ -31,16 +32,16 @@ chokidar_1.default.watch(`${configDir}/e2e/**/*.feature`, {
|
|
|
31
32
|
})
|
|
32
33
|
.on("change", async (event, path) => {
|
|
33
34
|
console.log(chalk_1.default.yellowBright("\nRefreshing test files..."));
|
|
34
|
-
await (0, runner_playwright_1.executePreprocessor)(tempDir, configDir);
|
|
35
|
+
await (0, runner_playwright_1.executePreprocessor)(tempDir, configDir, env);
|
|
35
36
|
console.log(chalk_1.default.yellowBright("Test files refreshed\n"));
|
|
36
37
|
})
|
|
37
38
|
.on("add", async (path) => {
|
|
38
39
|
console.log(chalk_1.default.yellowBright(`\nFile ${path} has been added`));
|
|
39
|
-
await (0, runner_playwright_1.executePreprocessor)(tempDir, configDir);
|
|
40
|
+
await (0, runner_playwright_1.executePreprocessor)(tempDir, configDir, env);
|
|
40
41
|
console.log(chalk_1.default.yellowBright("Test files refreshed\n"));
|
|
41
42
|
})
|
|
42
43
|
.on("unlink", async (path) => {
|
|
43
44
|
console.log(chalk_1.default.yellowBright(`\nFile ${path} has been removed`));
|
|
44
|
-
await (0, runner_playwright_1.executePreprocessor)(tempDir, configDir);
|
|
45
|
+
await (0, runner_playwright_1.executePreprocessor)(tempDir, configDir, env);
|
|
45
46
|
console.log(chalk_1.default.yellowBright("Test files refreshed\n"));
|
|
46
47
|
});
|
|
@@ -14,6 +14,7 @@ const nanoid_1 = require("nanoid");
|
|
|
14
14
|
const chalk_1 = __importDefault(require("chalk"));
|
|
15
15
|
const chalk_table_1 = __importDefault(require("chalk-table"));
|
|
16
16
|
const uuv_custom_formatter_1 = require("./uuv-custom-formatter");
|
|
17
|
+
const tag_expressions_1 = __importDefault(require("@cucumber/tag-expressions"));
|
|
17
18
|
const NANOS_IN_SECOND = 1000000000;
|
|
18
19
|
const NANOS_IN_MILLISSECOND = 1000000;
|
|
19
20
|
var GeneratedReportType;
|
|
@@ -331,6 +332,8 @@ class UuvPlaywrightReporterHelper {
|
|
|
331
332
|
initializeCucumberReportNdJson(suite, featureFiles) {
|
|
332
333
|
featureFiles.forEach(featureFile => {
|
|
333
334
|
const originalFile = this.getOriginalFeatureFile(featureFile);
|
|
335
|
+
const tagsParameter = this.getTagsParameter();
|
|
336
|
+
const tagsExpression = (0, tag_expressions_1.default)(tagsParameter ? tagsParameter : "");
|
|
334
337
|
if (originalFile) {
|
|
335
338
|
const currentEnvelopes = (0, gherkin_1.generateMessages)(fs_1.default.readFileSync(originalFile).toString(), originalFile, messages_1.SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN, {
|
|
336
339
|
includeSource: true,
|
|
@@ -339,7 +342,20 @@ class UuvPlaywrightReporterHelper {
|
|
|
339
342
|
includePickles: true
|
|
340
343
|
});
|
|
341
344
|
const currentQuery = new gherkin_utils_1.Query();
|
|
342
|
-
currentEnvelopes
|
|
345
|
+
currentEnvelopes
|
|
346
|
+
.map(envelope => {
|
|
347
|
+
if (tagsParameter && envelope?.gherkinDocument?.feature) {
|
|
348
|
+
envelope.gherkinDocument.feature.children = envelope.gherkinDocument.feature.children.filter(child => child.scenario?.tags && tagsExpression.evaluate(child.scenario.tags.map(tag => tag.name)));
|
|
349
|
+
}
|
|
350
|
+
return envelope;
|
|
351
|
+
})
|
|
352
|
+
.filter(envelope => {
|
|
353
|
+
if (tagsParameter && envelope?.pickle?.steps) {
|
|
354
|
+
return tagsExpression.evaluate(envelope.pickle.tags.map(pickleTag => pickleTag.name));
|
|
355
|
+
}
|
|
356
|
+
return true;
|
|
357
|
+
})
|
|
358
|
+
.forEach(envelope => currentQuery.update(envelope));
|
|
343
359
|
this.queries.set(originalFile, currentQuery);
|
|
344
360
|
this.envelopes = this.envelopes.concat(currentEnvelopes);
|
|
345
361
|
this.featureFileAndTestCaseStatusMap.set(originalFile, []);
|
|
@@ -403,9 +419,23 @@ class UuvPlaywrightReporterHelper {
|
|
|
403
419
|
await formatter.parseCucumberJson(inputMessageFile, outputFormattedFileJson);
|
|
404
420
|
}
|
|
405
421
|
generateHtmlReportFromJson(reportDirHtml, reportDirJson) {
|
|
422
|
+
const UNKNOWN_VALUE = "unknown";
|
|
406
423
|
multiple_cucumber_html_reporter_1.default.generate({
|
|
407
424
|
jsonDir: reportDirJson,
|
|
408
|
-
reportPath: reportDirHtml
|
|
425
|
+
reportPath: reportDirHtml,
|
|
426
|
+
metadata: {
|
|
427
|
+
browser: {
|
|
428
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
429
|
+
// @ts-ignore
|
|
430
|
+
name: process.env.browser,
|
|
431
|
+
version: UNKNOWN_VALUE
|
|
432
|
+
},
|
|
433
|
+
device: UNKNOWN_VALUE,
|
|
434
|
+
platform: {
|
|
435
|
+
name: UNKNOWN_VALUE,
|
|
436
|
+
version: UNKNOWN_VALUE,
|
|
437
|
+
},
|
|
438
|
+
},
|
|
409
439
|
});
|
|
410
440
|
}
|
|
411
441
|
async generateHtmlReport() {
|
|
@@ -520,5 +550,10 @@ class UuvPlaywrightReporterHelper {
|
|
|
520
550
|
teamcityAddCustomField(fieldName, value) {
|
|
521
551
|
return `${fieldName}='${value}'`;
|
|
522
552
|
}
|
|
553
|
+
getTagsParameter() {
|
|
554
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
555
|
+
// @ts-ignore
|
|
556
|
+
return process.env.TAGS;
|
|
557
|
+
}
|
|
523
558
|
}
|
|
524
559
|
exports.default = UuvPlaywrightReporterHelper;
|
|
@@ -15,7 +15,8 @@ class UuvPlawrightReporter {
|
|
|
15
15
|
console.info(chalk_1.default.yellow(`Starting the run with ${suite.allTests().length} tests`));
|
|
16
16
|
}
|
|
17
17
|
onError(error) {
|
|
18
|
-
console.
|
|
18
|
+
console.error(chalk_1.default.red("An error occured: "));
|
|
19
|
+
console.dir(error);
|
|
19
20
|
}
|
|
20
21
|
onTestBegin(test, result) {
|
|
21
22
|
const startTimestamp = this.helper.getTimestamp(result.startTime);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uuv/playwright",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"author": "Louis Fredice NJAKO MOLOM (https://github.com/luifr10) & Stanley SERVICAL (https://github.com/stanlee974)",
|
|
6
6
|
"description": "A solution to facilitate the writing and execution of E2E tests understandable by any human being using cucumber(BDD) and playwright",
|
|
@@ -38,15 +38,16 @@
|
|
|
38
38
|
"postinstall": "node postinstall.js",
|
|
39
39
|
"serverTest:run": "ts-node playwright/run-test-app.ts 9002",
|
|
40
40
|
"test:run": "ts-node test.ts run",
|
|
41
|
-
"test:open": "ts-node test.ts open",
|
|
41
|
+
"test:open": "ts-node test.ts open --",
|
|
42
42
|
"test:alone:run": "npx playwright test",
|
|
43
43
|
"test:format": "ts-node format-cucumber-report.ts",
|
|
44
|
-
"test": "npm run test:run"
|
|
44
|
+
"test": "npm run test:run --"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@cucumber/cucumber": "9.6.0",
|
|
48
|
+
"@cucumber/tag-expressions": "^6.0.0",
|
|
48
49
|
"@playwright/test": "1.33.0",
|
|
49
|
-
"@uuv/runner-commons": "2.0
|
|
50
|
+
"@uuv/runner-commons": "2.1.0",
|
|
50
51
|
"axe-core": "4.8.2",
|
|
51
52
|
"axe-playwright": "1.2.3",
|
|
52
53
|
"chalk": "4.1.2",
|