kryptheon 0.1.10 → 0.1.12
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/bin/kryptheon.js +49 -11
- package/kryptheon-baselines.js +176 -0
- package/kryptheon-fixture.js +432 -388
- package/kryptheon-project.js +199 -0
- package/kryptheon-reporter.js +138 -5
- package/kryptheon-secrets.js +191 -191
- package/kryptheon-signature.js +11 -11
- package/package.json +74 -67
package/kryptheon-secrets.js
CHANGED
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
// Which environment variables a recording needs, and whether they are there.
|
|
2
|
-
//
|
|
3
|
-
// Why this exists. When a password is typed during recording it is taken out
|
|
4
|
-
// of the spec and replaced with `process.env.KRYPTHEON_PASSWORD || ''`. If
|
|
5
|
-
// nobody ever creates the .env file, that expression quietly evaluates to an
|
|
6
|
-
// empty string: the run submits a blank password, the login fails for the most
|
|
7
|
-
// boring reason imaginable, and the report blames the app. Two hours can go
|
|
8
|
-
// into that before anyone thinks to check the environment.
|
|
9
|
-
//
|
|
10
|
-
// So the names are read out of the spec before anything runs, and a recording
|
|
11
|
-
// whose secret is missing is not run at all - a blank password is never sent.
|
|
12
|
-
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
|
|
16
|
-
// The exact shape the scrubber writes. Anything else in the file that happens
|
|
17
|
-
// to mention process.env is the author's own business and left alone.
|
|
18
|
-
const SECRET_USE = /process\.env\.([A-Z][A-Z0-9_]*)\s*\|\|\s*(['"])\2/g;
|
|
19
|
-
|
|
20
|
-
/** Every environment variable this recording falls back to an empty string for. */
|
|
21
|
-
function requiredSecrets(source) {
|
|
22
|
-
const out = [];
|
|
23
|
-
const seen = Object.create(null);
|
|
24
|
-
let match;
|
|
25
|
-
SECRET_USE.lastIndex = 0;
|
|
26
|
-
while ((match = SECRET_USE.exec(String(source || '')))) {
|
|
27
|
-
const name = match[1];
|
|
28
|
-
if (seen[name]) continue;
|
|
29
|
-
seen[name] = true;
|
|
30
|
-
out.push(name);
|
|
31
|
-
}
|
|
32
|
-
return out;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Reads a .env file into a plain object. Never throws: a missing file simply
|
|
37
|
-
* means nothing was defined there.
|
|
38
|
-
*
|
|
39
|
-
* Values are not expanded or unescaped beyond stripping one layer of quotes -
|
|
40
|
-
* this only has to answer "is it set", and guessing at more would be a second
|
|
41
|
-
* dialect of .env for people to trip over.
|
|
42
|
-
*/
|
|
43
|
-
function readEnvFile(file) {
|
|
44
|
-
const out = Object.create(null);
|
|
45
|
-
let raw;
|
|
46
|
-
try {
|
|
47
|
-
raw = fs.readFileSync(file, 'utf8');
|
|
48
|
-
} catch (e) {
|
|
49
|
-
return out;
|
|
50
|
-
}
|
|
51
|
-
// A byte order mark would otherwise become part of the first name.
|
|
52
|
-
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
|
|
53
|
-
for (const line of raw.split('\n')) {
|
|
54
|
-
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
55
|
-
if (!match) continue;
|
|
56
|
-
out[match[1]] = match[2].replace(/^(['"])([\s\S]*)\1$/, '$2');
|
|
57
|
-
}
|
|
58
|
-
return out;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** A value that is present and not blank. An empty variable is not set. */
|
|
62
|
-
function isSet(value) {
|
|
63
|
-
return typeof value === 'string' && value.trim() !== '';
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Which of the names a recording needs are missing, looking at both the real
|
|
68
|
-
* environment and the project's .env - the same two places the test itself
|
|
69
|
-
* will look when it runs.
|
|
70
|
-
*/
|
|
71
|
-
function missingSecrets(names, options) {
|
|
72
|
-
const opts = options || {};
|
|
73
|
-
const env = opts.env || process.env;
|
|
74
|
-
const fromFile = opts.envFile ? readEnvFile(opts.envFile) : {};
|
|
75
|
-
return (names || []).filter((name) => !isSet(env[name]) && !isSet(fromFile[name]));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* How to create the file without a byte order mark.
|
|
80
|
-
*
|
|
81
|
-
* PowerShell's own `Out-File -Encoding utf8` writes one, and so does Notepad,
|
|
82
|
-
* which is why this spells out a command that does not rather than leaving it
|
|
83
|
-
* to whatever the person reaches for first.
|
|
84
|
-
*/
|
|
85
|
-
function howToWriteEnv(names, platform) {
|
|
86
|
-
const line = names.map((name) => name + '=yourpassword').join('\\n');
|
|
87
|
-
if ((platform || process.platform) === 'win32') {
|
|
88
|
-
return [
|
|
89
|
-
' In PowerShell, this writes it without a byte order mark:',
|
|
90
|
-
' [IO.File]::WriteAllText("$PWD\\.env", "' + line + '`n")',
|
|
91
|
-
'',
|
|
92
|
-
' Avoid Out-File and Notepad here: both add a mark that makes the first',
|
|
93
|
-
' line unreadable, and the password would silently come out blank.',
|
|
94
|
-
];
|
|
95
|
-
}
|
|
96
|
-
return [' For example:', " printf '" + line.replace(/\\n/g, "\\n") + "\\n' > .env"];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** What to tell somebody who has not created the file yet. */
|
|
100
|
-
function missingSecretLines(specName, names, options) {
|
|
101
|
-
const opts = options || {};
|
|
102
|
-
const many = names.length > 1;
|
|
103
|
-
const lines = [
|
|
104
|
-
' Skipped ' + specName,
|
|
105
|
-
' This recording needs a password' + (many ? ' and other secrets' : '') + ', and there is none set.',
|
|
106
|
-
'',
|
|
107
|
-
];
|
|
108
|
-
if (opts.examplePath) {
|
|
109
|
-
lines.push(' There is a ' + opts.examplePath + ' here already. Put the real value in it');
|
|
110
|
-
lines.push(' and save it as .env, in the same folder.');
|
|
111
|
-
} else {
|
|
112
|
-
lines.push(' Create a .env file in this folder containing:');
|
|
113
|
-
for (const name of names) lines.push(' ' + name + '=yourpassword');
|
|
114
|
-
}
|
|
115
|
-
lines.push('');
|
|
116
|
-
for (const line of howToWriteEnv(names, opts.platform)) lines.push(line);
|
|
117
|
-
lines.push('');
|
|
118
|
-
lines.push(' Without it the recording would sign in with a blank password and');
|
|
119
|
-
lines.push(' fail for a reason that has nothing to do with your app, so it was');
|
|
120
|
-
lines.push(' not run. Keep .env out of version control.');
|
|
121
|
-
return lines;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Writes .env.example so nobody has to get the encoding right by hand.
|
|
126
|
-
*
|
|
127
|
-
* Plain UTF-8, no byte order mark. Never overwrites: the file may already hold
|
|
128
|
-
* names from an earlier recording, and clobbering it would lose them.
|
|
129
|
-
*/
|
|
130
|
-
function writeEnvExample(dir, names) {
|
|
131
|
-
const file = path.join(dir, '.env.example');
|
|
132
|
-
if (fs.existsSync(file)) return null;
|
|
133
|
-
const body = (names || []).map((name) => name + '=yourpassword').join('\n') + '\n';
|
|
134
|
-
try {
|
|
135
|
-
fs.writeFileSync(file, body, { encoding: 'utf8' });
|
|
136
|
-
return file;
|
|
137
|
-
} catch (e) {
|
|
138
|
-
return null; // a convenience, never a reason to fail a recording
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Everything kryptheon leaves in a project that should not be committed: the
|
|
143
|
-
// real password, and three files that are machine state rather than source.
|
|
144
|
-
const GITIGNORE_ENTRIES = ['.env', 'kryptheon-baselines.json', 'kryptheon-history.jsonl', 'test-results/'];
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Adds the missing entries to an existing .gitignore. Returns what it added.
|
|
148
|
-
*
|
|
149
|
-
* Only touches a file that is already there - creating one in a project that
|
|
150
|
-
* has deliberately gone without is not this tool's decision to make.
|
|
151
|
-
*/
|
|
152
|
-
function updateGitignore(dir, entries) {
|
|
153
|
-
const file = path.join(dir, '.gitignore');
|
|
154
|
-
let raw;
|
|
155
|
-
try {
|
|
156
|
-
raw = fs.readFileSync(file, { encoding: 'utf8' });
|
|
157
|
-
} catch (e) {
|
|
158
|
-
return [];
|
|
159
|
-
}
|
|
160
|
-
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
|
|
161
|
-
const present = new Set(
|
|
162
|
-
raw
|
|
163
|
-
.split('\n')
|
|
164
|
-
.map((line) => line.trim().replace(/^\/+/, '').replace(/\/+$/, ''))
|
|
165
|
-
.filter(Boolean),
|
|
166
|
-
);
|
|
167
|
-
const wanted = entries || GITIGNORE_ENTRIES;
|
|
168
|
-
const missing = wanted.filter((entry) => !present.has(entry.replace(/\/+$/, '')));
|
|
169
|
-
if (!missing.length) return [];
|
|
170
|
-
const needsNewline = raw.length && !raw.endsWith('\n');
|
|
171
|
-
const block = (needsNewline ? '\n' : '') + '\n# kryptheon\n' + missing.join('\n') + '\n';
|
|
172
|
-
try {
|
|
173
|
-
fs.appendFileSync(file, block, { encoding: 'utf8' });
|
|
174
|
-
return missing;
|
|
175
|
-
} catch (e) {
|
|
176
|
-
return [];
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
module.exports = {
|
|
181
|
-
requiredSecrets: requiredSecrets,
|
|
182
|
-
readEnvFile: readEnvFile,
|
|
183
|
-
missingSecrets: missingSecrets,
|
|
184
|
-
missingSecretLines: missingSecretLines,
|
|
185
|
-
howToWriteEnv: howToWriteEnv,
|
|
186
|
-
writeEnvExample: writeEnvExample,
|
|
187
|
-
updateGitignore: updateGitignore,
|
|
188
|
-
GITIGNORE_ENTRIES: GITIGNORE_ENTRIES,
|
|
189
|
-
isSet: isSet,
|
|
190
|
-
envFileFor: (dir) => path.join(dir, '.env'),
|
|
191
|
-
};
|
|
1
|
+
// Which environment variables a recording needs, and whether they are there.
|
|
2
|
+
//
|
|
3
|
+
// Why this exists. When a password is typed during recording it is taken out
|
|
4
|
+
// of the spec and replaced with `process.env.KRYPTHEON_PASSWORD || ''`. If
|
|
5
|
+
// nobody ever creates the .env file, that expression quietly evaluates to an
|
|
6
|
+
// empty string: the run submits a blank password, the login fails for the most
|
|
7
|
+
// boring reason imaginable, and the report blames the app. Two hours can go
|
|
8
|
+
// into that before anyone thinks to check the environment.
|
|
9
|
+
//
|
|
10
|
+
// So the names are read out of the spec before anything runs, and a recording
|
|
11
|
+
// whose secret is missing is not run at all - a blank password is never sent.
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// The exact shape the scrubber writes. Anything else in the file that happens
|
|
17
|
+
// to mention process.env is the author's own business and left alone.
|
|
18
|
+
const SECRET_USE = /process\.env\.([A-Z][A-Z0-9_]*)\s*\|\|\s*(['"])\2/g;
|
|
19
|
+
|
|
20
|
+
/** Every environment variable this recording falls back to an empty string for. */
|
|
21
|
+
function requiredSecrets(source) {
|
|
22
|
+
const out = [];
|
|
23
|
+
const seen = Object.create(null);
|
|
24
|
+
let match;
|
|
25
|
+
SECRET_USE.lastIndex = 0;
|
|
26
|
+
while ((match = SECRET_USE.exec(String(source || '')))) {
|
|
27
|
+
const name = match[1];
|
|
28
|
+
if (seen[name]) continue;
|
|
29
|
+
seen[name] = true;
|
|
30
|
+
out.push(name);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reads a .env file into a plain object. Never throws: a missing file simply
|
|
37
|
+
* means nothing was defined there.
|
|
38
|
+
*
|
|
39
|
+
* Values are not expanded or unescaped beyond stripping one layer of quotes -
|
|
40
|
+
* this only has to answer "is it set", and guessing at more would be a second
|
|
41
|
+
* dialect of .env for people to trip over.
|
|
42
|
+
*/
|
|
43
|
+
function readEnvFile(file) {
|
|
44
|
+
const out = Object.create(null);
|
|
45
|
+
let raw;
|
|
46
|
+
try {
|
|
47
|
+
raw = fs.readFileSync(file, 'utf8');
|
|
48
|
+
} catch (e) {
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
// A byte order mark would otherwise become part of the first name.
|
|
52
|
+
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
|
|
53
|
+
for (const line of raw.split('\n')) {
|
|
54
|
+
const match = line.match(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
55
|
+
if (!match) continue;
|
|
56
|
+
out[match[1]] = match[2].replace(/^(['"])([\s\S]*)\1$/, '$2');
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A value that is present and not blank. An empty variable is not set. */
|
|
62
|
+
function isSet(value) {
|
|
63
|
+
return typeof value === 'string' && value.trim() !== '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Which of the names a recording needs are missing, looking at both the real
|
|
68
|
+
* environment and the project's .env - the same two places the test itself
|
|
69
|
+
* will look when it runs.
|
|
70
|
+
*/
|
|
71
|
+
function missingSecrets(names, options) {
|
|
72
|
+
const opts = options || {};
|
|
73
|
+
const env = opts.env || process.env;
|
|
74
|
+
const fromFile = opts.envFile ? readEnvFile(opts.envFile) : {};
|
|
75
|
+
return (names || []).filter((name) => !isSet(env[name]) && !isSet(fromFile[name]));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* How to create the file without a byte order mark.
|
|
80
|
+
*
|
|
81
|
+
* PowerShell's own `Out-File -Encoding utf8` writes one, and so does Notepad,
|
|
82
|
+
* which is why this spells out a command that does not rather than leaving it
|
|
83
|
+
* to whatever the person reaches for first.
|
|
84
|
+
*/
|
|
85
|
+
function howToWriteEnv(names, platform) {
|
|
86
|
+
const line = names.map((name) => name + '=yourpassword').join('\\n');
|
|
87
|
+
if ((platform || process.platform) === 'win32') {
|
|
88
|
+
return [
|
|
89
|
+
' In PowerShell, this writes it without a byte order mark:',
|
|
90
|
+
' [IO.File]::WriteAllText("$PWD\\.env", "' + line + '`n")',
|
|
91
|
+
'',
|
|
92
|
+
' Avoid Out-File and Notepad here: both add a mark that makes the first',
|
|
93
|
+
' line unreadable, and the password would silently come out blank.',
|
|
94
|
+
];
|
|
95
|
+
}
|
|
96
|
+
return [' For example:', " printf '" + line.replace(/\\n/g, "\\n") + "\\n' > .env"];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** What to tell somebody who has not created the file yet. */
|
|
100
|
+
function missingSecretLines(specName, names, options) {
|
|
101
|
+
const opts = options || {};
|
|
102
|
+
const many = names.length > 1;
|
|
103
|
+
const lines = [
|
|
104
|
+
' Skipped ' + specName,
|
|
105
|
+
' This recording needs a password' + (many ? ' and other secrets' : '') + ', and there is none set.',
|
|
106
|
+
'',
|
|
107
|
+
];
|
|
108
|
+
if (opts.examplePath) {
|
|
109
|
+
lines.push(' There is a ' + opts.examplePath + ' here already. Put the real value in it');
|
|
110
|
+
lines.push(' and save it as .env, in the same folder.');
|
|
111
|
+
} else {
|
|
112
|
+
lines.push(' Create a .env file in this folder containing:');
|
|
113
|
+
for (const name of names) lines.push(' ' + name + '=yourpassword');
|
|
114
|
+
}
|
|
115
|
+
lines.push('');
|
|
116
|
+
for (const line of howToWriteEnv(names, opts.platform)) lines.push(line);
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push(' Without it the recording would sign in with a blank password and');
|
|
119
|
+
lines.push(' fail for a reason that has nothing to do with your app, so it was');
|
|
120
|
+
lines.push(' not run. Keep .env out of version control.');
|
|
121
|
+
return lines;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Writes .env.example so nobody has to get the encoding right by hand.
|
|
126
|
+
*
|
|
127
|
+
* Plain UTF-8, no byte order mark. Never overwrites: the file may already hold
|
|
128
|
+
* names from an earlier recording, and clobbering it would lose them.
|
|
129
|
+
*/
|
|
130
|
+
function writeEnvExample(dir, names) {
|
|
131
|
+
const file = path.join(dir, '.env.example');
|
|
132
|
+
if (fs.existsSync(file)) return null;
|
|
133
|
+
const body = (names || []).map((name) => name + '=yourpassword').join('\n') + '\n';
|
|
134
|
+
try {
|
|
135
|
+
fs.writeFileSync(file, body, { encoding: 'utf8' });
|
|
136
|
+
return file;
|
|
137
|
+
} catch (e) {
|
|
138
|
+
return null; // a convenience, never a reason to fail a recording
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Everything kryptheon leaves in a project that should not be committed: the
|
|
143
|
+
// real password, and three files that are machine state rather than source.
|
|
144
|
+
const GITIGNORE_ENTRIES = ['.env', 'kryptheon-baselines.json', 'kryptheon-history.jsonl', 'test-results/'];
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Adds the missing entries to an existing .gitignore. Returns what it added.
|
|
148
|
+
*
|
|
149
|
+
* Only touches a file that is already there - creating one in a project that
|
|
150
|
+
* has deliberately gone without is not this tool's decision to make.
|
|
151
|
+
*/
|
|
152
|
+
function updateGitignore(dir, entries) {
|
|
153
|
+
const file = path.join(dir, '.gitignore');
|
|
154
|
+
let raw;
|
|
155
|
+
try {
|
|
156
|
+
raw = fs.readFileSync(file, { encoding: 'utf8' });
|
|
157
|
+
} catch (e) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
if (raw.charCodeAt(0) === 0xfeff) raw = raw.slice(1);
|
|
161
|
+
const present = new Set(
|
|
162
|
+
raw
|
|
163
|
+
.split('\n')
|
|
164
|
+
.map((line) => line.trim().replace(/^\/+/, '').replace(/\/+$/, ''))
|
|
165
|
+
.filter(Boolean),
|
|
166
|
+
);
|
|
167
|
+
const wanted = entries || GITIGNORE_ENTRIES;
|
|
168
|
+
const missing = wanted.filter((entry) => !present.has(entry.replace(/\/+$/, '')));
|
|
169
|
+
if (!missing.length) return [];
|
|
170
|
+
const needsNewline = raw.length && !raw.endsWith('\n');
|
|
171
|
+
const block = (needsNewline ? '\n' : '') + '\n# kryptheon\n' + missing.join('\n') + '\n';
|
|
172
|
+
try {
|
|
173
|
+
fs.appendFileSync(file, block, { encoding: 'utf8' });
|
|
174
|
+
return missing;
|
|
175
|
+
} catch (e) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = {
|
|
181
|
+
requiredSecrets: requiredSecrets,
|
|
182
|
+
readEnvFile: readEnvFile,
|
|
183
|
+
missingSecrets: missingSecrets,
|
|
184
|
+
missingSecretLines: missingSecretLines,
|
|
185
|
+
howToWriteEnv: howToWriteEnv,
|
|
186
|
+
writeEnvExample: writeEnvExample,
|
|
187
|
+
updateGitignore: updateGitignore,
|
|
188
|
+
GITIGNORE_ENTRIES: GITIGNORE_ENTRIES,
|
|
189
|
+
isSet: isSet,
|
|
190
|
+
envFileFor: (dir) => path.join(dir, '.env'),
|
|
191
|
+
};
|
package/kryptheon-signature.js
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
// showing. Not a screenshot and not a pixel comparison - just the words a
|
|
3
3
|
// person would use to say what is on screen.
|
|
4
4
|
//
|
|
5
|
-
// Why it exists.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
5
|
+
// Why it exists. A baseline used to remember only the final address and the
|
|
6
|
+
// page title. In a single-page app with a catch-all route those both stay the
|
|
7
|
+
// same when a flow breaks: a login that no longer logs anyone in still lands on
|
|
8
|
+
// the same URL under the same title, so the baseline saw no difference and the
|
|
9
|
+
// run was reported as fine. The signature is the thing that notices - the
|
|
10
|
+
// headings, buttons, links and fields on a page change even when the address
|
|
11
|
+
// does not.
|
|
12
12
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
13
|
+
// A signature is part of the baseline: saved on a pass, compared on the next
|
|
14
|
+
// run, and a difference fails the test. KRYPTHEON_DEBUG_SIGNATURE=1 additionally
|
|
15
|
+
// prints what was captured, which is only ever a way to see the reasoning.
|
|
16
16
|
|
|
17
17
|
const fs = require('fs');
|
|
18
18
|
|
|
@@ -495,7 +495,7 @@ function readRecorded(file) {
|
|
|
495
495
|
}
|
|
496
496
|
|
|
497
497
|
/**
|
|
498
|
-
* Prints
|
|
498
|
+
* Prints a signature, for when someone wants to see what was read.
|
|
499
499
|
*
|
|
500
500
|
* Goes to stderr so the reporter's own output, which is stdout, stays exactly
|
|
501
501
|
* as it was - a person or a script reading the report sees no difference.
|
package/package.json
CHANGED
|
@@ -1,67 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kryptheon",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Record what you do in your app and get plain-language reports when it breaks.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "commonjs",
|
|
7
|
-
"bin": {
|
|
8
|
-
"kryptheon": "bin/kryptheon.js"
|
|
9
|
-
},
|
|
10
|
-
"exports": {
|
|
11
|
-
".": "./kryptheon-fixture.js",
|
|
12
|
-
"./kryptheon-fixture": "./kryptheon-fixture.js",
|
|
13
|
-
"./kryptheon-reporter": "./kryptheon-reporter.js",
|
|
14
|
-
"./package.json": "./package.json",
|
|
15
|
-
"./kryptheon-replay": "./kryptheon-replay.js",
|
|
16
|
-
"./kryptheon-signature": "./kryptheon-signature.js",
|
|
17
|
-
"./kryptheon-secrets": "./kryptheon-secrets.js"
|
|
18
|
-
},
|
|
19
|
-
"files": [
|
|
20
|
-
"bin/",
|
|
21
|
-
"kryptheon-fixture.js",
|
|
22
|
-
"kryptheon-reporter.js",
|
|
23
|
-
"kryptheon-signature.js",
|
|
24
|
-
"kryptheon-secrets.js",
|
|
25
|
-
"playwright.config.js",
|
|
26
|
-
"README.md",
|
|
27
|
-
"kryptheon-replay.js",
|
|
28
|
-
"kryptheon-selectors.js",
|
|
29
|
-
"kryptheon-recordings.js",
|
|
30
|
-
"kryptheon-cleanup.js"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"check
|
|
42
|
-
"check:
|
|
43
|
-
"check:
|
|
44
|
-
"check:
|
|
45
|
-
"check:
|
|
46
|
-
"check:
|
|
47
|
-
"
|
|
48
|
-
"check:
|
|
49
|
-
"check:
|
|
50
|
-
"check:
|
|
51
|
-
"check:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "kryptheon",
|
|
3
|
+
"version": "0.1.12",
|
|
4
|
+
"description": "Record what you do in your app and get plain-language reports when it breaks.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kryptheon": "bin/kryptheon.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./kryptheon-fixture.js",
|
|
12
|
+
"./kryptheon-fixture": "./kryptheon-fixture.js",
|
|
13
|
+
"./kryptheon-reporter": "./kryptheon-reporter.js",
|
|
14
|
+
"./package.json": "./package.json",
|
|
15
|
+
"./kryptheon-replay": "./kryptheon-replay.js",
|
|
16
|
+
"./kryptheon-signature": "./kryptheon-signature.js",
|
|
17
|
+
"./kryptheon-secrets": "./kryptheon-secrets.js"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"bin/",
|
|
21
|
+
"kryptheon-fixture.js",
|
|
22
|
+
"kryptheon-reporter.js",
|
|
23
|
+
"kryptheon-signature.js",
|
|
24
|
+
"kryptheon-secrets.js",
|
|
25
|
+
"playwright.config.js",
|
|
26
|
+
"README.md",
|
|
27
|
+
"kryptheon-replay.js",
|
|
28
|
+
"kryptheon-selectors.js",
|
|
29
|
+
"kryptheon-recordings.js",
|
|
30
|
+
"kryptheon-cleanup.js",
|
|
31
|
+
"kryptheon-baselines.js",
|
|
32
|
+
"kryptheon-project.js"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.6.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@playwright/test": "^1.62.1"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"check": "node kryptheon-reporter.check.js && node kryptheon-signature.check.js && node kryptheon-signature.stream.check.js && node kryptheon-signature.baseline.check.js && node kryptheon-signature.settle.check.js && node kryptheon-signature.number.check.js && node kryptheon-env.check.js && node kryptheon-watchdog.check.js && node kryptheon-record.check.js && node kryptheon-selectors.check.js && node kryptheon-recordings.check.js && node kryptheon-cleanup.check.js && node kryptheon-orphan.check.js && node kryptheon-project.check.js && node kryptheon-load.check.js && node kryptheon-package.check.js",
|
|
42
|
+
"check:reporter": "node kryptheon-reporter.check.js",
|
|
43
|
+
"check:signature": "node kryptheon-signature.check.js",
|
|
44
|
+
"check:stream": "node kryptheon-signature.stream.check.js",
|
|
45
|
+
"check:baseline": "node kryptheon-signature.baseline.check.js",
|
|
46
|
+
"check:settle": "node kryptheon-signature.settle.check.js",
|
|
47
|
+
"check:number": "node kryptheon-signature.number.check.js",
|
|
48
|
+
"check:env": "node kryptheon-env.check.js",
|
|
49
|
+
"check:watchdog": "node kryptheon-watchdog.check.js",
|
|
50
|
+
"check:record": "node kryptheon-record.check.js",
|
|
51
|
+
"check:selectors": "node kryptheon-selectors.check.js",
|
|
52
|
+
"check:recordings": "node kryptheon-recordings.check.js",
|
|
53
|
+
"check:cleanup": "node kryptheon-cleanup.check.js",
|
|
54
|
+
"check:orphan": "node kryptheon-orphan.check.js",
|
|
55
|
+
"check:project": "node kryptheon-project.check.js",
|
|
56
|
+
"check:load": "node kryptheon-load.check.js",
|
|
57
|
+
"check:package": "node kryptheon-package.check.js",
|
|
58
|
+
"prepublishOnly": "node kryptheon-package.check.js"
|
|
59
|
+
},
|
|
60
|
+
"keywords": [
|
|
61
|
+
"playwright",
|
|
62
|
+
"testing",
|
|
63
|
+
"end-to-end",
|
|
64
|
+
"cli"
|
|
65
|
+
],
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/jhashantanu987-beep/kryptheon-cli.git"
|
|
69
|
+
},
|
|
70
|
+
"bugs": {
|
|
71
|
+
"url": "https://github.com/jhashantanu987-beep/kryptheon-cli/issues"
|
|
72
|
+
},
|
|
73
|
+
"homepage": "https://github.com/jhashantanu987-beep/kryptheon-cli#readme"
|
|
74
|
+
}
|