@maeris/maeris-player 0.1.0 → 0.1.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/README.md +68 -16
- package/package.json +4 -1
- package/src/cli/run.js +26 -0
- package/src/cli/setup-cert.js +2 -0
- package/src/runner/playwrightAdapter.js +17 -9
package/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# Maeris Player
|
|
2
2
|
|
|
3
|
-
Local Playwright-based runner for Maeris
|
|
3
|
+
Local Playwright-based runner for Maeris **Run in Browser**.
|
|
4
|
+
Start the runner once, then trigger runs from the Maeris web app.
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
- Node.js 18+
|
|
8
|
+
- macOS / Windows / Linux
|
|
9
|
+
- Access to the `@maeris` npm scope (this package may be private)
|
|
4
10
|
|
|
5
11
|
## Goals
|
|
6
12
|
- Replay recorder steps through the Chrome extension in a local browser window.
|
|
@@ -13,31 +19,77 @@ Local Playwright-based runner for Maeris "Run in Browser".
|
|
|
13
19
|
3. **Playwright adapter (`src/runner/playwrightAdapter.js`)** performs clicks, inputs, navigations, assertions; captures screenshots on failure.
|
|
14
20
|
4. **Event contract (`src/runner/events.js`)** defines the message names (`STEP_STARTED`, `STEP_PASSED`, `STEP_FAILED`, `NAVIGATION`, `REPLAY_COMPLETE`, etc.).
|
|
15
21
|
|
|
16
|
-
##
|
|
22
|
+
## Quick start
|
|
17
23
|
```bash
|
|
18
|
-
|
|
24
|
+
npx @maeris/maeris-player
|
|
25
|
+
```
|
|
19
26
|
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
This starts the local runner on `ws://localhost:8090` (headed by default).
|
|
28
|
+
Keep it running, then click **Run in Browser** in the Maeris UI.
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
If you prefer a global install:
|
|
31
|
+
```bash
|
|
32
|
+
npm i -g @maeris/maeris-player
|
|
33
|
+
maeris-player
|
|
25
34
|
```
|
|
26
35
|
|
|
27
|
-
##
|
|
36
|
+
## How users run tests
|
|
37
|
+
1. Start the runner:
|
|
38
|
+
```bash
|
|
39
|
+
npx @maeris/maeris-player
|
|
40
|
+
```
|
|
41
|
+
2. Open the Maeris app and click **Run in Browser**.
|
|
42
|
+
3. The runner opens a local browser window and executes the steps.
|
|
43
|
+
|
|
44
|
+
## Production usage options
|
|
45
|
+
### Option A — Extension proxy (recommended)
|
|
46
|
+
Works from `https://` without TLS.
|
|
47
|
+
The Maeris extension connects to the local runner and proxies events.
|
|
48
|
+
|
|
49
|
+
1. Install the Maeris Chrome extension.
|
|
50
|
+
2. Start the runner:
|
|
51
|
+
```bash
|
|
52
|
+
npx @maeris/maeris-player
|
|
53
|
+
```
|
|
54
|
+
3. Click **Run in Browser** in the Maeris app.
|
|
55
|
+
|
|
56
|
+
### Option B — TLS direct (no extension)
|
|
57
|
+
Connect directly from an `https://` web app using `wss://localhost`.
|
|
58
|
+
|
|
28
59
|
```bash
|
|
60
|
+
npx @maeris/maeris-player setup-cert
|
|
61
|
+
|
|
62
|
+
# macOS/Linux
|
|
63
|
+
MAERIS_TLS_CERT="$HOME/.maeris-player/certs/localhost.cert.pem" \
|
|
64
|
+
MAERIS_TLS_KEY="$HOME/.maeris-player/certs/localhost.key.pem" \
|
|
29
65
|
npx @maeris/maeris-player
|
|
30
66
|
```
|
|
31
67
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## HTTPS / WSS support (for prod without extension)
|
|
35
|
-
If you want to connect from an `https://` web app directly to the runner, start it with TLS:
|
|
68
|
+
Then trust the certificate as instructed by `setup-cert`.
|
|
69
|
+
The runner will listen on `wss://localhost:8090`.
|
|
36
70
|
|
|
71
|
+
Tip: if you used the default cert location, you can also run:
|
|
37
72
|
```bash
|
|
38
|
-
|
|
39
|
-
MAERIS_TLS_CERT=/path/to/cert.pem MAERIS_TLS_KEY=/path/to/key.pem npx @maeris/maeris-player
|
|
73
|
+
maeris-player --tls
|
|
40
74
|
```
|
|
41
75
|
|
|
42
|
-
|
|
43
|
-
|
|
76
|
+
## Troubleshooting
|
|
77
|
+
- If the Maeris web app shows "Extension required" on an `https://` page:
|
|
78
|
+
- use the Maeris Player (TLS) option in the modal, or
|
|
79
|
+
- install the extension (Option A).
|
|
80
|
+
- If runs don't start, confirm the runner is listening:
|
|
81
|
+
- `ws://localhost:8090` for non-TLS
|
|
82
|
+
- `wss://localhost:8090` for TLS
|
|
83
|
+
- If publish fails with 2FA errors, see "Publishing" below.
|
|
84
|
+
|
|
85
|
+
## Publishing
|
|
86
|
+
This is published as a public scoped package (`@maeris/maeris-player`).
|
|
87
|
+
|
|
88
|
+
If your npm org enforces 2FA, you will need either:
|
|
89
|
+
- an OTP at publish time (`npm publish --otp=123456`), or
|
|
90
|
+
- a granular access token with "bypass 2FA for publish" enabled.
|
|
91
|
+
|
|
92
|
+
First publish (or if npm requires it for scoped packages):
|
|
93
|
+
```bash
|
|
94
|
+
npm publish --access public
|
|
95
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maeris/maeris-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local Playwright-based runner for Maeris",
|
|
6
6
|
"scripts": {
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"bin": {
|
|
13
13
|
"maeris-player": "src/cli/run.js"
|
|
14
14
|
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
15
18
|
"license": "MIT",
|
|
16
19
|
"dependencies": {
|
|
17
20
|
"playwright": "^1.44.0",
|
package/src/cli/run.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
4
5
|
const yargs = require('yargs/yargs');
|
|
5
6
|
const { hideBin } = require('yargs/helpers');
|
|
6
7
|
const { Runner } = require('../runner');
|
|
@@ -38,6 +39,12 @@ const argv = yargs(hideBin(process.argv))
|
|
|
38
39
|
default: true,
|
|
39
40
|
describe: 'Start the runner server and wait for UI commands',
|
|
40
41
|
})
|
|
42
|
+
.option('tls', {
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
default: false,
|
|
45
|
+
describe:
|
|
46
|
+
'Start in TLS mode for HTTPS pages (uses ~/.maeris-player/certs unless MAERIS_TLS_* env vars are set)',
|
|
47
|
+
})
|
|
41
48
|
.option('cert-dir', {
|
|
42
49
|
type: 'string',
|
|
43
50
|
describe: 'Directory to store TLS certs for setup-cert',
|
|
@@ -45,6 +52,21 @@ const argv = yargs(hideBin(process.argv))
|
|
|
45
52
|
.help()
|
|
46
53
|
.parse();
|
|
47
54
|
|
|
55
|
+
function applyTlsDefaults() {
|
|
56
|
+
if (process.env.MAERIS_TLS_CERT && process.env.MAERIS_TLS_KEY) return;
|
|
57
|
+
const baseDir = path.join(os.homedir(), '.maeris-player', 'certs');
|
|
58
|
+
const certPath = path.join(baseDir, 'localhost.cert.pem');
|
|
59
|
+
const keyPath = path.join(baseDir, 'localhost.key.pem');
|
|
60
|
+
if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
|
61
|
+
process.env.MAERIS_TLS_CERT = certPath;
|
|
62
|
+
process.env.MAERIS_TLS_KEY = keyPath;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
throw new Error(
|
|
66
|
+
`TLS cert not found. Run \"maeris-player setup-cert\" first (expected ${certPath} and ${keyPath}).`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
48
70
|
function loadSteps(filePath) {
|
|
49
71
|
const resolved = path.resolve(filePath);
|
|
50
72
|
if (!fs.existsSync(resolved)) {
|
|
@@ -65,6 +87,10 @@ function loadSteps(filePath) {
|
|
|
65
87
|
return;
|
|
66
88
|
}
|
|
67
89
|
|
|
90
|
+
if (argv.tls) {
|
|
91
|
+
applyTlsDefaults();
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
const steps = argv.steps ? loadSteps(argv.steps) : [];
|
|
69
95
|
const options = {
|
|
70
96
|
steps,
|
package/src/cli/setup-cert.js
CHANGED
|
@@ -73,6 +73,8 @@ function printInstructions({ certPath, keyPath }) {
|
|
|
73
73
|
console.log(` ${linux}`);
|
|
74
74
|
console.log('\nThen start the runner with:');
|
|
75
75
|
console.log(` MAERIS_TLS_CERT="${certPath}" MAERIS_TLS_KEY="${keyPath}" npx @maeris/maeris-player\n`);
|
|
76
|
+
console.log('Or (if you installed globally):');
|
|
77
|
+
console.log(' maeris-player --tls\n');
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
module.exports = {
|
|
@@ -42,7 +42,7 @@ class PlaywrightAdapter extends EventEmitter {
|
|
|
42
42
|
await this.performStep(this.page, step);
|
|
43
43
|
this.emit('event', createStepPayload(EVENTS.STEP_PASSED, step));
|
|
44
44
|
} catch (error) {
|
|
45
|
-
const screenshot = await page.screenshot({ fullPage: true });
|
|
45
|
+
const screenshot = await this.page.screenshot({ fullPage: true });
|
|
46
46
|
this.emit(
|
|
47
47
|
'event',
|
|
48
48
|
createStepPayload(EVENTS.STEP_FAILED, step, {
|
|
@@ -119,15 +119,23 @@ class PlaywrightAdapter extends EventEmitter {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
applyNth(locator, step) {
|
|
122
|
+
const parseNth = (value) => {
|
|
123
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
124
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
125
|
+
const parsed = Number.parseInt(value, 10);
|
|
126
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
};
|
|
130
|
+
|
|
122
131
|
const nth =
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return nth !== null ? locator.nth(nth) : locator;
|
|
132
|
+
parseNth(step.nth_appearance) ??
|
|
133
|
+
parseNth(step.nthAppearance) ??
|
|
134
|
+
parseNth(step.step_index);
|
|
135
|
+
|
|
136
|
+
// If nth is missing, use first() to avoid strict-mode violations on broad selectors.
|
|
137
|
+
if (nth === null || nth < 0) return locator.first();
|
|
138
|
+
return locator.nth(nth);
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
async performStep(page, step) {
|