@maeris/maeris-player 0.1.0 → 0.1.2

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 CHANGED
@@ -1,43 +1,88 @@
1
1
  # Maeris Player
2
2
 
3
- Local Playwright-based runner for Maeris "Run in Browser".
3
+ Local Playwright-based runner for Maeris **Run in Browser**.
4
+ Start the runner once, then trigger runs from the Maeris web app.
4
5
 
5
- ## Goals
6
- - Replay recorder steps through the Chrome extension in a local browser window.
7
- - Mirror the cloud runner as closely as possible (status updates, assertions, screenshots).
8
- - Provide a lightweight CLI or UI shim that interfaces with the extension via WebSocket messages.
6
+ ## Prerequisites
7
+ - Node.js 18+
8
+ - macOS / Windows / Linux
9
9
 
10
- ## Architecture
11
- 1. **CLI (`src/cli/run.js`)** starts a WebSocket server and waits for the UI to send `RUN_STEPS`, or replays a local JSON file when `--steps` is provided.
12
- 2. **Runner core (`src/runner/index.js`)** normalizes steps, opens Playwright, streams `STEP_*`/`REPLAY_*` events over WebSocket (port 8090).
13
- 3. **Playwright adapter (`src/runner/playwrightAdapter.js`)** performs clicks, inputs, navigations, assertions; captures screenshots on failure.
14
- 4. **Event contract (`src/runner/events.js`)** defines the message names (`STEP_STARTED`, `STEP_PASSED`, `STEP_FAILED`, `NAVIGATION`, `REPLAY_COMPLETE`, etc.).
15
-
16
- ## Running locally
10
+ ## Quick start
17
11
  ```bash
18
- npm install
12
+ npx @maeris/maeris-player
13
+ ```
19
14
 
20
- # listen for Run in Browser traffic from the web app (headed by default)
21
- npm start
15
+ This starts the local runner on `ws://localhost:8090` (headed by default).
16
+ Keep it running, then click **Run in Browser** in the Maeris UI.
22
17
 
23
- # replay a recorded JSON file
24
- npm start -- --steps path/to/recorded.json --url https://demo-app/ --browser chromium
18
+ If you prefer a global install:
19
+ ```bash
20
+ npm i -g @maeris/maeris-player
21
+ maeris-player
25
22
  ```
26
23
 
27
- ## Usage via npm
24
+ ## How users run tests
25
+ 1. Start the runner:
26
+ ```bash
27
+ npx @maeris/maeris-player
28
+ ```
29
+ 2. Open the Maeris app and click **Run in Browser**.
30
+ 3. The runner opens a local browser window and executes the steps.
31
+
32
+ ## Production usage options
33
+ ### Option A — Extension proxy (recommended)
34
+ Works from `https://` without TLS.
35
+ The Maeris extension connects to the local runner and proxies events.
36
+
37
+ 1. Install the Maeris Chrome extension.
38
+ 2. Start the runner:
39
+ ```bash
40
+ npx @maeris/maeris-player
41
+ ```
42
+ 3. Click **Run in Browser** in the Maeris app.
43
+
44
+ ### Option B — TLS direct (no extension)
45
+ Connect directly from an `https://` web app using `wss://localhost`.
46
+
28
47
  ```bash
48
+ npx @maeris/maeris-player setup-cert
49
+
50
+ # macOS/Linux
51
+ MAERIS_TLS_CERT="$HOME/.maeris-player/certs/localhost.cert.pem" \
52
+ MAERIS_TLS_KEY="$HOME/.maeris-player/certs/localhost.key.pem" \
29
53
  npx @maeris/maeris-player
30
54
  ```
31
55
 
32
- This starts the local runner on `ws://localhost:8090`. Keep it running, then click **Run in Browser** in the Maeris UI.
56
+ Then trust the certificate as instructed by `setup-cert`.
57
+ The runner will listen on `wss://localhost:8090`.
58
+
59
+ Tip: if you used the default cert location, you can also run:
60
+ ```bash
61
+ maeris-player --tls
62
+ ```
63
+
64
+ ## Troubleshooting
65
+ - If the Maeris web app shows "Extension required" on an `https://` page:
66
+ - use the Maeris Player (TLS) option in the modal, or
67
+ - install the extension (Option A).
68
+ - If runs don't start, confirm the runner is listening:
69
+ - `ws://localhost:8090` for non-TLS
70
+ - `wss://localhost:8090` for TLS
33
71
 
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:
72
+ ## Maintainers
73
+ ### Publishing
74
+ This is published as a public scoped package (`@maeris/maeris-player`).
36
75
 
76
+ If your npm org enforces 2FA, you will need either:
77
+ - an OTP at publish time (`npm publish --otp=123456`), or
78
+ - a granular access token with "bypass 2FA for publish" enabled.
79
+
80
+ First publish (or if npm requires it for scoped packages):
37
81
  ```bash
38
- npx @maeris/maeris-player setup-cert
39
- MAERIS_TLS_CERT=/path/to/cert.pem MAERIS_TLS_KEY=/path/to/key.pem npx @maeris/maeris-player
82
+ npm publish --access public
40
83
  ```
41
84
 
42
- The runner will listen on `wss://localhost:8090`. You will need to trust the certificate in your OS/browser.
43
- # maeris-player
85
+ ### Architecture (for contributors)
86
+ 1. `src/cli/run.js` starts a WebSocket server and waits for the UI to send `RUN_STEPS`, or replays a local JSON file when `--steps` is provided.
87
+ 2. `src/runner/index.js` normalizes steps, opens Playwright, and streams `STEP_*` / `REPLAY_*` events over WebSocket (port 8090).
88
+ 3. `src/runner/playwrightAdapter.js` performs clicks, inputs, navigations, assertions, and captures screenshots on failure.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maeris/maeris-player",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "Local Playwright-based runner for Maeris",
6
6
  "scripts": {
@@ -12,11 +12,17 @@
12
12
  "bin": {
13
13
  "maeris-player": "src/cli/run.js"
14
14
  },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
15
18
  "license": "MIT",
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
16
22
  "dependencies": {
17
23
  "playwright": "^1.44.0",
18
24
  "selfsigned": "^2.4.1",
19
25
  "ws": "^8.13.0",
20
- "yargs": "^20.0.0"
26
+ "yargs": "^17.7.2"
21
27
  }
22
28
  }
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,
@@ -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
- typeof step.nth_appearance === 'number'
124
- ? step.nth_appearance
125
- : typeof step.nthAppearance === 'number'
126
- ? step.nthAppearance
127
- : typeof step.step_index === 'number'
128
- ? step.step_index
129
- : null;
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) {