@apica-io/asm-playwright-runner 1.0.0-dev.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/asm-playwright-runner.iml +8 -0
- package/dist/actions.js +47 -0
- package/dist/cli/index.js +97 -0
- package/dist/lib/helper.js +109 -0
- package/dist/lib/parser.js +309 -0
- package/dist/loader.js +5 -0
- package/dist/model/result.js +1 -0
- package/dist/model/runnerConfig.js +27 -0
- package/dist/model/traceModel.js +2 -0
- package/dist/runner.js +280 -0
- package/package.json +47 -0
- package/samples/betsson.spec.ts +86 -0
- package/samples/example.json +10 -0
- package/samples/google.json +6 -0
- package/samples/playwright-multi-script.spec.ts +60 -0
- package/samples/playwright-script.spec.ts +9 -0
- package/samples/playwright-test-script.spec.ts +18 -0
- package/samples/r.json +16 -0
- package/src/actions.ts +42 -0
- package/src/cli/index.ts +74 -0
- package/src/lib/helper.ts +81 -0
- package/src/lib/parser.ts +341 -0
- package/src/model/runnerConfig.ts +41 -0
- package/src/model/traceModel.ts +150 -0
- package/src/runner.ts +272 -0
- package/tsconfig.json +23 -0
package/dist/runner.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.PlaywrightRunner = void 0;
|
|
39
|
+
const fs_1 = __importDefault(require("fs"));
|
|
40
|
+
const path_1 = __importDefault(require("path"));
|
|
41
|
+
const playwright_1 = require("playwright");
|
|
42
|
+
const actions_1 = require("./actions");
|
|
43
|
+
const runnerConfig_1 = require("./model/runnerConfig");
|
|
44
|
+
const parser_1 = require("./lib/parser");
|
|
45
|
+
const log4js_1 = require("log4js");
|
|
46
|
+
const node_child_process_1 = require("node:child_process");
|
|
47
|
+
const helper_1 = require("./lib/helper");
|
|
48
|
+
const RESULT_FILE = "trace-model.json";
|
|
49
|
+
class PlaywrightRunner {
|
|
50
|
+
constructor() {
|
|
51
|
+
this.browser = null;
|
|
52
|
+
this.context = null;
|
|
53
|
+
this.page = null;
|
|
54
|
+
this.options = null;
|
|
55
|
+
this.logger = (0, log4js_1.getLogger)(PlaywrightRunner.name);
|
|
56
|
+
}
|
|
57
|
+
init(collectionPath, options) {
|
|
58
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
59
|
+
var _a;
|
|
60
|
+
this.logger.info("Initializing PlaywrightRunner...");
|
|
61
|
+
// Normalize resultDir
|
|
62
|
+
if (!options.resultDir || options.resultDir.trim() === "") {
|
|
63
|
+
options.resultDir = runnerConfig_1.ResultDir.BASE_DIR;
|
|
64
|
+
this.logger.debug("No resultDir specified. Defaulting to 'results'.");
|
|
65
|
+
}
|
|
66
|
+
this.options = options;
|
|
67
|
+
// Detect script type
|
|
68
|
+
this.options.scriptType = yield this.detectScriptType(collectionPath);
|
|
69
|
+
this.options.isValidScript = this.options.scriptType !== undefined;
|
|
70
|
+
if (!this.options.isValidScript) {
|
|
71
|
+
this.logger.warn("Script is not valid.");
|
|
72
|
+
process.exit(9);
|
|
73
|
+
}
|
|
74
|
+
// Skip browser setup for Playwright Test files
|
|
75
|
+
if (this.options.scriptType === runnerConfig_1.ScriptType.PLAYWRIGHT_TEST) {
|
|
76
|
+
this.logger.info("Playwright Test detected. Skipping manual browser setup.");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Browser setup
|
|
80
|
+
const browserType = options.browser || runnerConfig_1.BrowserType.CHROMIUM;
|
|
81
|
+
const headless = options.headless;
|
|
82
|
+
this.logger.debug(`Browser type: ${browserType}, headless: ${headless}`);
|
|
83
|
+
this.browser = yield (browserType === runnerConfig_1.BrowserType.FIREFOX ? playwright_1.firefox :
|
|
84
|
+
browserType === runnerConfig_1.BrowserType.WEBKIT ? playwright_1.webkit :
|
|
85
|
+
playwright_1.chromium).launch(Object.assign({ headless }, (options.chromiumPath && { executablePath: options.chromiumPath })));
|
|
86
|
+
this.logger.info("Browser launched successfully.");
|
|
87
|
+
let headers = {};
|
|
88
|
+
if (options.extraHTTPHeaders) {
|
|
89
|
+
try {
|
|
90
|
+
headers = JSON.parse(options.extraHTTPHeaders);
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
this.logger.warn("Invalid JSON for extraHTTPHeaders, ignoring:", options.extraHTTPHeaders);
|
|
94
|
+
headers = {}; // fallback to empty headers
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this.context = yield this.browser.newContext(Object.assign(Object.assign({ ignoreHTTPSErrors: true }, (options.sslClientCert && {
|
|
98
|
+
clientCert: {
|
|
99
|
+
cert: options.sslClientCert,
|
|
100
|
+
key: options.sslClientKey,
|
|
101
|
+
passphrase: options.sslClientPassphrase
|
|
102
|
+
}
|
|
103
|
+
})), { extraHTTPHeaders: headers }));
|
|
104
|
+
this.logger.info("Browser context created.");
|
|
105
|
+
yield this.context.tracing.start({ snapshots: true, screenshots: true, sources: true });
|
|
106
|
+
this.logger.info("Tracing started (snapshots, screenshots, sources enabled).");
|
|
107
|
+
this.page = yield this.context.newPage();
|
|
108
|
+
// @ts-ignore
|
|
109
|
+
if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.scriptType) != runnerConfig_1.ScriptType.PLAYWRIGHT_TEST && this.options.timeout != null) {
|
|
110
|
+
this.page.setDefaultTimeout(Number(this.options.timeout));
|
|
111
|
+
}
|
|
112
|
+
this.logger.info("New page created.");
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
run(collectionPath, options) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
var _a;
|
|
118
|
+
this.logger.info(`Running collection: ${collectionPath}`);
|
|
119
|
+
// Default trace path(s)
|
|
120
|
+
let tracePaths = [
|
|
121
|
+
options.resultDir
|
|
122
|
+
? path_1.default.join(options.resultDir, "trace.zip")
|
|
123
|
+
: "trace.zip"
|
|
124
|
+
];
|
|
125
|
+
switch ((_a = this.options) === null || _a === void 0 ? void 0 : _a.scriptType) {
|
|
126
|
+
case runnerConfig_1.ScriptType.JSON: {
|
|
127
|
+
const flow = (0, helper_1.loadFlow)(collectionPath);
|
|
128
|
+
this.logger.info(`Loaded flow: ${flow.name} with ${flow.steps.length} steps`);
|
|
129
|
+
for (const [index, step] of flow.steps.entries()) {
|
|
130
|
+
const handler = actions_1.actionRegistry[step.action];
|
|
131
|
+
this.logger.debug(`Executing step ${index}: action=${step.action}`);
|
|
132
|
+
try {
|
|
133
|
+
if (!handler)
|
|
134
|
+
this.logger.error(`Unknown action: ${step.action}`);
|
|
135
|
+
if (!this.page) {
|
|
136
|
+
this.logger.error("Page not initialized. Did you call init()?");
|
|
137
|
+
process.exit(9);
|
|
138
|
+
}
|
|
139
|
+
yield handler(this.page, step);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
this.logger.error(`Step ${index} failed: ${err.message}`, err);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case runnerConfig_1.ScriptType.PLAYWRIGHT: {
|
|
148
|
+
this.logger.info("Detected plain Playwright script with default export.");
|
|
149
|
+
const mod = yield Promise.resolve(`${path_1.default.resolve(collectionPath)}`).then(s => __importStar(require(s)));
|
|
150
|
+
const fn = mod.default;
|
|
151
|
+
if (typeof fn !== "function") {
|
|
152
|
+
throw new Error("Script does not export a default function");
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
yield fn(this.page);
|
|
156
|
+
this.logger.info("Script execution finished.");
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
this.logger.error(`Script execution failed: ${err.message}`);
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
case runnerConfig_1.ScriptType.PLAYWRIGHT_TEST: {
|
|
164
|
+
this.logger.info("Detected Playwright Test file. Running via CLI...");
|
|
165
|
+
const args = [
|
|
166
|
+
"playwright",
|
|
167
|
+
"test",
|
|
168
|
+
collectionPath,
|
|
169
|
+
"--browser", options.browser || runnerConfig_1.BrowserType.CHROMIUM,
|
|
170
|
+
"--output", options.resultDir || "results",
|
|
171
|
+
"--trace", "on"
|
|
172
|
+
];
|
|
173
|
+
if (options.timeout != null)
|
|
174
|
+
args.push("--timeout", options.timeout);
|
|
175
|
+
if (!options.headless)
|
|
176
|
+
args.push("--headed");
|
|
177
|
+
// if (options.verbose) args.push("--verbose");
|
|
178
|
+
this.logger.debug(`Spawning Playwright CLI: npx ${args.join(" ")}`);
|
|
179
|
+
yield new Promise((resolve) => {
|
|
180
|
+
const proc = (0, node_child_process_1.spawn)("npx", args, { stdio: "inherit", shell: true });
|
|
181
|
+
proc.on("close", (code) => {
|
|
182
|
+
if (code === 0) {
|
|
183
|
+
this.logger.info("Playwright Test finished successfully.");
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.logger.error(`Playwright Test exited with code ${code}`);
|
|
187
|
+
}
|
|
188
|
+
tracePaths = (0, helper_1.collectTraceFiles)(options.resultDir || "results");
|
|
189
|
+
resolve();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
default:
|
|
195
|
+
this.logger.error("Unsupported or invalid script type.");
|
|
196
|
+
process.exit(9);
|
|
197
|
+
}
|
|
198
|
+
this.logger.info("Stopping tracing...");
|
|
199
|
+
yield this.traceProcess(tracePaths);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
traceProcess(tracePaths) {
|
|
203
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
205
|
+
if (!(tracePaths === null || tracePaths === void 0 ? void 0 : tracePaths.length)) {
|
|
206
|
+
this.logger.warn("No trace paths provided.");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
// Only stop tracing if this.context is active and we’re handling a live run
|
|
210
|
+
if (this.context && ((_a = this.options) === null || _a === void 0 ? void 0 : _a.scriptType) != runnerConfig_1.ScriptType.PLAYWRIGHT_TEST) {
|
|
211
|
+
yield this.context.tracing.stop({
|
|
212
|
+
path: tracePaths[0]
|
|
213
|
+
});
|
|
214
|
+
this.logger.info(`Trace saved to ${tracePaths}`);
|
|
215
|
+
}
|
|
216
|
+
const models = {};
|
|
217
|
+
for (const tracePath of tracePaths) {
|
|
218
|
+
try {
|
|
219
|
+
this.logger.info(`Parsing trace: ${tracePath}`);
|
|
220
|
+
const traceModel = yield (0, parser_1.prepareTraceModel)(tracePath, (_b = this.options) === null || _b === void 0 ? void 0 : _b.logLevel);
|
|
221
|
+
const runName = path_1.default.basename(path_1.default.dirname(tracePath));
|
|
222
|
+
models[runName] = traceModel;
|
|
223
|
+
const individualPath = path_1.default.join(path_1.default.dirname(tracePath), RESULT_FILE);
|
|
224
|
+
fs_1.default.writeFileSync(individualPath, JSON.stringify({ "__self": traceModel }, null, 2), "utf-8");
|
|
225
|
+
this.logger.debug(`Individual trace model written to ${individualPath}`);
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
this.logger.error(`Failed to process trace ${tracePath}: ${err.message}`, err);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Write combined model if more than one trace
|
|
232
|
+
if (((_c = this.options) === null || _c === void 0 ? void 0 : _c.scriptType) == runnerConfig_1.ScriptType.PLAYWRIGHT_TEST) {
|
|
233
|
+
const combinedPath = ((_d = this.options) === null || _d === void 0 ? void 0 : _d.resultDir)
|
|
234
|
+
? path_1.default.join(this.options.resultDir, RESULT_FILE)
|
|
235
|
+
: RESULT_FILE;
|
|
236
|
+
fs_1.default.writeFileSync(combinedPath, JSON.stringify(models, null, 2), "utf-8");
|
|
237
|
+
this.logger.info(`Combined trace model written to ${combinedPath}`);
|
|
238
|
+
}
|
|
239
|
+
const resourceDir = (_f = (_e = this.options) === null || _e === void 0 ? void 0 : _e.resultDir) !== null && _f !== void 0 ? _f : runnerConfig_1.ResultDir.BASE_DIR;
|
|
240
|
+
const finalZipPath = path_1.default.join(resourceDir, `artifacts.zip`);
|
|
241
|
+
yield (0, parser_1.zipResources)(finalZipPath, path_1.default.join(resourceDir, runnerConfig_1.ResultDir.SCREENSHOT), path_1.default.join(resourceDir, runnerConfig_1.ResultDir.SOURCE));
|
|
242
|
+
if ((_g = this.options) === null || _g === void 0 ? void 0 : _g.returnResult) {
|
|
243
|
+
console.log(JSON.stringify(models));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
detectScriptType(collectionPath) {
|
|
248
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
249
|
+
try {
|
|
250
|
+
const mod = yield Promise.resolve(`${path_1.default.resolve(collectionPath)}`).then(s => __importStar(require(s)));
|
|
251
|
+
if (typeof mod.default === "function") {
|
|
252
|
+
this.logger.debug("Detected plain Playwright script (default export).");
|
|
253
|
+
return runnerConfig_1.ScriptType.PLAYWRIGHT;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (_a) {
|
|
257
|
+
this.logger.debug("Dynamic import failed, falling back to file content check.");
|
|
258
|
+
}
|
|
259
|
+
if (collectionPath.endsWith(".json")) {
|
|
260
|
+
this.logger.debug("Detected JSON flow.");
|
|
261
|
+
return runnerConfig_1.ScriptType.JSON;
|
|
262
|
+
}
|
|
263
|
+
const fileContent = fs_1.default.readFileSync(collectionPath, "utf-8");
|
|
264
|
+
if (fileContent.includes("test(")) {
|
|
265
|
+
this.logger.debug("Detected Playwright Test file.");
|
|
266
|
+
return runnerConfig_1.ScriptType.PLAYWRIGHT_TEST;
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
close() {
|
|
272
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
273
|
+
var _a;
|
|
274
|
+
this.logger.info("Closing browser...");
|
|
275
|
+
yield ((_a = this.browser) === null || _a === void 0 ? void 0 : _a.close());
|
|
276
|
+
this.logger.info("Browser closed.");
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
exports.PlaywrightRunner = PlaywrightRunner;
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@apica-io/asm-playwright-runner",
|
|
3
|
+
"version": "1.0.0-dev.1",
|
|
4
|
+
"description": "CLI wrapper for Playwright collections or scripts with dynamic actions, config, and test result models.",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"asm-playwright-runner": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "ts-node src/cli/ index.ts",
|
|
12
|
+
"dev": "ts-node-dev --respawn src/cli/index.ts",
|
|
13
|
+
"test:playwright-script": "ts-node src/cli samples/playwright-script.spec.ts --timeout 10000 --browser chromium --headless -v -r result1 -l debug",
|
|
14
|
+
"test:playwright/test-script": "ts-node src/cli samples/playwright-test-script.spec.ts --browser chromium --headless -r result2 -l debug",
|
|
15
|
+
"test:playwright-multi-script": "ts-node src/cli samples/playwright-multi-script.spec.ts --browser chromium --headless -v -r result3 -l debug",
|
|
16
|
+
"test:betsson-script": "ts-node src/cli samples/betsson.spec.ts --browser chromium -r result-betsson --headless false -l debug --extraHTTPHeaders {\\\"x-obg-bypass-fabric\\\":\\\"1\\\",\\\"x-obg-experiments\\\":\\\"b1ccAR1gW0f8\\\"}",
|
|
17
|
+
"test:playwright-json": "ts-node src/cli samples/example.json --browser chromium --headless -v -r results3",
|
|
18
|
+
"test:google": "ts-node src/cli samples/google.json --browser chromium --headless -v -r playwright-multi-script",
|
|
19
|
+
"test:google-firefox": "ts-node src/cli samples/google.json --browser firefox --headless -v -r playwright-multi-script"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"playwright",
|
|
23
|
+
"cli",
|
|
24
|
+
"automation",
|
|
25
|
+
"testing",
|
|
26
|
+
"runner",
|
|
27
|
+
"Apica"
|
|
28
|
+
],
|
|
29
|
+
"author": "Bhavik, Apica",
|
|
30
|
+
"license": "ISC",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"commander": "^11.0.0",
|
|
33
|
+
"playwright": "^1.60.0",
|
|
34
|
+
"playwright-core": "^1.59.1",
|
|
35
|
+
"log4js": "^6.3.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@playwright/test": "^1.59.1",
|
|
39
|
+
"@types/leaflet": "^1.9.15",
|
|
40
|
+
"@types/node": "^25.6.2",
|
|
41
|
+
"ts-node": "^10.9.2",
|
|
42
|
+
"ts-node-dev": "^2.0.0",
|
|
43
|
+
"typescript": "^5.4.0",
|
|
44
|
+
"unzipper": "^0.12.3",
|
|
45
|
+
"archiver": "^5.3.2"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { expect, Page } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
export default async function example(page: Page) {
|
|
4
|
+
|
|
5
|
+
// Open site
|
|
6
|
+
await page.goto(
|
|
7
|
+
"https://caba.betsson.bet.ar/?source=apica",
|
|
8
|
+
{
|
|
9
|
+
waitUntil: "networkidle"
|
|
10
|
+
}
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
// Wait for header
|
|
14
|
+
await page.waitForSelector(
|
|
15
|
+
"site-header-version-manager"
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
// Login button inside shadow dom
|
|
19
|
+
const loginButton = page.locator(`
|
|
20
|
+
router-link-v2[data-test-id="login-button"]
|
|
21
|
+
>> fds-button_control
|
|
22
|
+
>> fds-button
|
|
23
|
+
>> button[data-test-id="btn-1-button"]
|
|
24
|
+
`);
|
|
25
|
+
|
|
26
|
+
await loginButton.waitFor({
|
|
27
|
+
state: 'visible',
|
|
28
|
+
timeout: 60000
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await expect(loginButton).toBeVisible();
|
|
32
|
+
|
|
33
|
+
await loginButton.click();
|
|
34
|
+
|
|
35
|
+
// Email input
|
|
36
|
+
const emailInput = page.locator(`
|
|
37
|
+
site-root_default
|
|
38
|
+
>> fds-dialog
|
|
39
|
+
>> account-login_fabric_wrapper
|
|
40
|
+
>> account-login-fabric
|
|
41
|
+
>> #email-input
|
|
42
|
+
`);
|
|
43
|
+
|
|
44
|
+
await emailInput.waitFor({
|
|
45
|
+
state: 'visible',
|
|
46
|
+
timeout: 60000
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await expect(emailInput).toBeVisible();
|
|
50
|
+
|
|
51
|
+
await emailInput.fill(
|
|
52
|
+
"noc@betssongroup.com"
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Password input
|
|
56
|
+
const passwordInput = page.locator(`
|
|
57
|
+
site-root_default
|
|
58
|
+
>> fds-dialog
|
|
59
|
+
>> account-login_fabric_wrapper
|
|
60
|
+
>> account-login-fabric
|
|
61
|
+
>> #password-input
|
|
62
|
+
`);
|
|
63
|
+
|
|
64
|
+
await expect(passwordInput).toBeVisible();
|
|
65
|
+
|
|
66
|
+
await passwordInput.fill(
|
|
67
|
+
"n0cT35tAcc0unt!!"
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Submit login
|
|
71
|
+
const submitButton = page.locator(`
|
|
72
|
+
site-root_default
|
|
73
|
+
>> fds-dialog
|
|
74
|
+
>> account-login_fabric_wrapper
|
|
75
|
+
>> account-login-fabric
|
|
76
|
+
>> fds-button_control
|
|
77
|
+
>> fds-button
|
|
78
|
+
`).last();
|
|
79
|
+
|
|
80
|
+
await expect(submitButton).toBeVisible();
|
|
81
|
+
|
|
82
|
+
await submitButton.click();
|
|
83
|
+
|
|
84
|
+
// Wait after login
|
|
85
|
+
await page.waitForTimeout(10000);
|
|
86
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "example",
|
|
3
|
+
"steps": [
|
|
4
|
+
{ "action": "goto", "url": "https://playwright.dev/" },
|
|
5
|
+
{ "action": "assertTitle", "expected": "Playwright" },
|
|
6
|
+
{ "action": "click", "selector": "role=link[name='Get started']" },
|
|
7
|
+
{ "action": "waitForSelector", "selector": "role=heading[name='Installation']" },
|
|
8
|
+
{ "action": "assertVisible", "selector": "role=heading[name='Installation']" }
|
|
9
|
+
]
|
|
10
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* To learn more about Playwright Test visit:
|
|
3
|
+
* https://checklyhq.com/docs/browser-checks/playwright-test/
|
|
4
|
+
* https://playwright.dev/docs/writing-tests
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { expect, test } = require('@playwright/test')
|
|
8
|
+
|
|
9
|
+
// Configure the Playwright Test timeout to 210 seconds,
|
|
10
|
+
// ensuring that longer tests conclude before Checkly's browser check timeout of 240 seconds.
|
|
11
|
+
// The default Playwright Test timeout is set at 30 seconds.
|
|
12
|
+
// For additional information on timeouts, visit: https://checklyhq.com/docs/browser-checks/timeouts/
|
|
13
|
+
test.setTimeout(210000)
|
|
14
|
+
|
|
15
|
+
// Set the action timeout to 10 seconds to quickly identify failing actions.
|
|
16
|
+
// By default Playwright Test has no timeout for actions (e.g. clicking an element).
|
|
17
|
+
test.use({ actionTimeout: 10000 })
|
|
18
|
+
|
|
19
|
+
// You can use test.describe to declare a group of related test cases
|
|
20
|
+
test.describe('Playwright home page', () => {
|
|
21
|
+
// The callback in test.beforeEach will be executed before each test.
|
|
22
|
+
test.beforeEach(async ({ page }) => {
|
|
23
|
+
// Each test will be given a new page instance navigated to the this URL
|
|
24
|
+
// For deployments Checkly will inject the deployment url as ENVIRONMENT_URL
|
|
25
|
+
await page.goto(process.env.ENVIRONMENT_URL || 'https://playwright.dev/')
|
|
26
|
+
})
|
|
27
|
+
// Other useful hooks: test.beforeAll, test.afterEach, test.afterAll
|
|
28
|
+
|
|
29
|
+
test('has a page title containing Playwright', async ({ page }) => {
|
|
30
|
+
// Expect a title "to contain" a substring.
|
|
31
|
+
await expect(page).toHaveTitle(/Playwright/)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("has a 'get started' link linking to the intro page", async ({ page }) => {
|
|
35
|
+
// Create a locator based on a text selector.
|
|
36
|
+
const getStarted = page.getByText('Get Started')
|
|
37
|
+
// Use the locator for runtime 2022.02:
|
|
38
|
+
// const getStarted = page.locator('text=Get Started')
|
|
39
|
+
|
|
40
|
+
// Expect an attribute "to be strictly equal" to the value.
|
|
41
|
+
await expect(getStarted).toHaveAttribute('href', '/docs/intro')
|
|
42
|
+
|
|
43
|
+
// Click the get started link.
|
|
44
|
+
await getStarted.click()
|
|
45
|
+
|
|
46
|
+
// Expects the URL to contain intro.
|
|
47
|
+
await expect(page).toHaveURL(/.*intro/)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test.describe('has Open Graph tags', () => {
|
|
51
|
+
const tags = ['description', 'title', 'url']
|
|
52
|
+
|
|
53
|
+
// You can create tests from an array, by calling "test()" in a loop
|
|
54
|
+
tags.forEach((tag) => {
|
|
55
|
+
test(`has the Open Graph tag "${tag}"`, async ({ page }) => {
|
|
56
|
+
await expect(page.locator(`meta[property="og:${tag}"]`)).toHaveCount(1)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import {expect, Page} from "playwright/test";
|
|
2
|
+
|
|
3
|
+
export default async function example(page: Page) {
|
|
4
|
+
await page.goto("https://playwright.dev/");
|
|
5
|
+
await expect(page).toHaveTitle(/Playwright/);
|
|
6
|
+
|
|
7
|
+
await page.getByRole("link", { name: "Get started" }).click();
|
|
8
|
+
await expect(page.getByRole("heading", { name: "Installation" })).toBeVisible();
|
|
9
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
test('has title', async ({ page }) => {
|
|
4
|
+
await page.goto('https://playwright.dev/');
|
|
5
|
+
|
|
6
|
+
// Expect a title "to contain" a substring.
|
|
7
|
+
await expect(page).toHaveTitle(/Playwright/);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('get started link', async ({ page }) => {
|
|
11
|
+
await page.goto('https://playwright.dev/');
|
|
12
|
+
|
|
13
|
+
// Click the get started link.
|
|
14
|
+
await page.getByRole('link', { name: 'Get started' }).click();
|
|
15
|
+
|
|
16
|
+
// Expects page to have a heading with the name of Installation.
|
|
17
|
+
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
|
|
18
|
+
});
|