proxymail-qa 0.4.0 → 0.5.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/README.md +7 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +59 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,13 @@ await qa.setPassword(address, { password: 'the-new-test-password' });
|
|
|
105
105
|
// its link goes, a stated expiry and more. Verdicts only, never its content.
|
|
106
106
|
const result = await qa.checkEmail(address, { type: 'link', expectHost: 'staging.example.com' });
|
|
107
107
|
expect(result.failed).toEqual([]);
|
|
108
|
+
|
|
109
|
+
// A run: this client's calls add what they saw, for a report on the pull
|
|
110
|
+
// request. In GitHub Actions, the Proxy Mail QA action does this for you and
|
|
111
|
+
// the client picks the run up from PROXYMAIL_QA_RUN.
|
|
112
|
+
await qa.startRun({ name: 'Sign-up tests' });
|
|
113
|
+
// …the tests…
|
|
114
|
+
await qa.finishRun({ failed: testsFailed });
|
|
108
115
|
```
|
|
109
116
|
|
|
110
117
|
When a wait comes back empty, the error says why as far as Proxy Mail QA can
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export interface ClientOptions {
|
|
|
2
2
|
apiKey?: string;
|
|
3
3
|
baseUrl?: string;
|
|
4
4
|
fetch?: typeof fetch;
|
|
5
|
+
run?: string | null;
|
|
5
6
|
}
|
|
6
7
|
export interface Persona {
|
|
7
8
|
first_name: string | null;
|
|
@@ -162,6 +163,37 @@ export interface NoEmail {
|
|
|
162
163
|
since: string;
|
|
163
164
|
until: string;
|
|
164
165
|
}
|
|
166
|
+
export interface RunInput {
|
|
167
|
+
name?: string;
|
|
168
|
+
source?: 'ci' | 'agent' | 'api';
|
|
169
|
+
meta?: {
|
|
170
|
+
repository?: string;
|
|
171
|
+
branch?: string;
|
|
172
|
+
commit?: string;
|
|
173
|
+
pull_request?: number;
|
|
174
|
+
workflow?: string;
|
|
175
|
+
job_url?: string;
|
|
176
|
+
agent?: string;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export interface RunEvent {
|
|
180
|
+
at: string;
|
|
181
|
+
kind: 'address' | 'email' | 'missing' | 'none' | 'count' | 'checks' | 'note';
|
|
182
|
+
address: string | null;
|
|
183
|
+
failed: boolean;
|
|
184
|
+
detail: Record<string, unknown>;
|
|
185
|
+
}
|
|
186
|
+
export interface Run {
|
|
187
|
+
id: string;
|
|
188
|
+
name: string;
|
|
189
|
+
source: 'ci' | 'agent' | 'api';
|
|
190
|
+
status: 'running' | 'passed' | 'failed';
|
|
191
|
+
meta: NonNullable<RunInput['meta']>;
|
|
192
|
+
started_at: string;
|
|
193
|
+
finished_at: string | null;
|
|
194
|
+
url: string;
|
|
195
|
+
events?: RunEvent[];
|
|
196
|
+
}
|
|
165
197
|
export declare class ProxyMailQAError extends Error {
|
|
166
198
|
readonly status: number;
|
|
167
199
|
readonly code: string;
|
|
@@ -174,6 +206,7 @@ export declare class ProxyMailQA {
|
|
|
174
206
|
private readonly apiKey;
|
|
175
207
|
private readonly baseUrl;
|
|
176
208
|
private readonly fetcher;
|
|
209
|
+
run: string | null;
|
|
177
210
|
constructor(options?: ClientOptions);
|
|
178
211
|
private request;
|
|
179
212
|
project(): Promise<Record<string, unknown>>;
|
|
@@ -217,5 +250,19 @@ export declare class ProxyMailQA {
|
|
|
217
250
|
label: string;
|
|
218
251
|
secret: string;
|
|
219
252
|
}>;
|
|
253
|
+
startRun(input?: RunInput): Promise<Run>;
|
|
254
|
+
getRun(id?: string): Promise<Run>;
|
|
255
|
+
runReport(id?: string): Promise<string>;
|
|
256
|
+
listRuns(limit?: number): Promise<Run[]>;
|
|
257
|
+
addRunNote(text: string, options?: {
|
|
258
|
+
failed?: boolean;
|
|
259
|
+
address?: string;
|
|
260
|
+
run?: string;
|
|
261
|
+
}): Promise<void>;
|
|
262
|
+
finishRun(options?: {
|
|
263
|
+
failed?: boolean;
|
|
264
|
+
run?: string;
|
|
265
|
+
}): Promise<Run>;
|
|
266
|
+
private requireRun;
|
|
220
267
|
private wait;
|
|
221
268
|
}
|
package/dist/index.js
CHANGED
|
@@ -40,23 +40,34 @@ export class ProxyMailQA {
|
|
|
40
40
|
apiKey;
|
|
41
41
|
baseUrl;
|
|
42
42
|
fetcher;
|
|
43
|
+
// The run this client's calls add to, if any.
|
|
44
|
+
run;
|
|
43
45
|
constructor(options = {}) {
|
|
44
|
-
const
|
|
45
|
-
const apiKey = options.apiKey ??
|
|
46
|
+
const env = globalThis.process?.env;
|
|
47
|
+
const apiKey = options.apiKey ?? env?.PROXYMAIL_QA_API_KEY;
|
|
46
48
|
if (!apiKey)
|
|
47
49
|
throw new Error('Pass apiKey or set PROXYMAIL_QA_API_KEY. Create a key in Project settings → API keys.');
|
|
48
50
|
this.apiKey = apiKey;
|
|
49
51
|
this.baseUrl = (options.baseUrl ?? 'https://api.proxymail.qa').replace(/\/+$/, '');
|
|
50
52
|
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
53
|
+
this.run = options.run === undefined ? env?.PROXYMAIL_QA_RUN?.trim() || null : options.run;
|
|
51
54
|
}
|
|
52
|
-
|
|
55
|
+
// retry: this call is asked again if nothing arrives, so the run only
|
|
56
|
+
// records the last try's "nothing arrived".
|
|
57
|
+
async request(method, path, body, retry = false) {
|
|
53
58
|
const response = await this.fetcher(`${this.baseUrl}/v1${path}`, {
|
|
54
59
|
method,
|
|
55
|
-
headers: {
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
62
|
+
...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
63
|
+
...(this.run ? { 'Proxy-Mail-QA-Run': this.run, ...(retry ? { 'Proxy-Mail-QA-Run-Retry': 'true' } : {}) } : {}),
|
|
64
|
+
},
|
|
56
65
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
57
66
|
});
|
|
58
67
|
if (response.status === 204)
|
|
59
68
|
return undefined;
|
|
69
|
+
if ((response.headers.get('Content-Type') ?? '').startsWith('text/markdown') && response.ok)
|
|
70
|
+
return (await response.text());
|
|
60
71
|
const payload = (await response.json().catch(() => ({})));
|
|
61
72
|
if (!response.ok) {
|
|
62
73
|
throw new ProxyMailQAError(response.status, payload.error ?? 'error', payload.message ?? `Request failed with ${response.status}`, payload.hint, payload.refused, payload.help);
|
|
@@ -164,7 +175,8 @@ export class ProxyMailQA {
|
|
|
164
175
|
for (;;) {
|
|
165
176
|
const left = Math.max(0, Math.min(MAX_WAIT_SECONDS, Math.ceil((deadline - Date.now()) / 1000)));
|
|
166
177
|
const query = countQuery({ ...options, since }, { timeout: String(left) });
|
|
167
|
-
const
|
|
178
|
+
const more = Date.now() + left * 1000 < deadline - 500;
|
|
179
|
+
const result = await this.request('GET', `/addresses/${encodeURIComponent(address)}/none${query}`, undefined, more);
|
|
168
180
|
// The server's since, so later requests watch from the same moment
|
|
169
181
|
// whatever this machine's clock says.
|
|
170
182
|
since = result.since;
|
|
@@ -187,6 +199,46 @@ export class ProxyMailQA {
|
|
|
187
199
|
savedPassword(address, label = 'password') {
|
|
188
200
|
return this.request('GET', `/addresses/${encodeURIComponent(address)}/password?label=${encodeURIComponent(label)}`);
|
|
189
201
|
}
|
|
202
|
+
// Starts a run: a CI job or agent session whose calls add what they saw
|
|
203
|
+
// (addresses, emails and how long after since, codes, a link's site and
|
|
204
|
+
// path, checks, and emails that never came). This client's later calls add
|
|
205
|
+
// to it; to add calls from other processes, pass them its id as
|
|
206
|
+
// PROXYMAIL_QA_RUN. The GitHub Action starts and finishes one for you.
|
|
207
|
+
async startRun(input = {}) {
|
|
208
|
+
const run = await this.request('POST', '/runs', input);
|
|
209
|
+
this.run = run.id;
|
|
210
|
+
return run;
|
|
211
|
+
}
|
|
212
|
+
getRun(id = this.requireRun()) {
|
|
213
|
+
return this.request('GET', `/runs/${encodeURIComponent(id)}`);
|
|
214
|
+
}
|
|
215
|
+
// The report in Markdown, for a pull request comment or a job summary.
|
|
216
|
+
runReport(id = this.requireRun()) {
|
|
217
|
+
return this.request('GET', `/runs/${encodeURIComponent(id)}?format=markdown`);
|
|
218
|
+
}
|
|
219
|
+
listRuns(limit) {
|
|
220
|
+
return this.request('GET', `/runs${limit ? `?limit=${limit}` : ''}`).then((r) => r.runs);
|
|
221
|
+
}
|
|
222
|
+
// A line of your own in the run, such as a test that failed for a reason
|
|
223
|
+
// of its own. failed: true fails the run.
|
|
224
|
+
addRunNote(text, options = {}) {
|
|
225
|
+
const id = options.run ?? this.requireRun();
|
|
226
|
+
return this.request('POST', `/runs/${encodeURIComponent(id)}/notes`, { text, failed: options.failed ?? false, address: options.address });
|
|
227
|
+
}
|
|
228
|
+
// Passed unless an email was missing, came when none should have, failed
|
|
229
|
+
// a check, or failed says your tests failed.
|
|
230
|
+
async finishRun(options = {}) {
|
|
231
|
+
const id = options.run ?? this.requireRun();
|
|
232
|
+
const run = await this.request('POST', `/runs/${encodeURIComponent(id)}/finish`, { failed: options.failed ?? false });
|
|
233
|
+
if (this.run === id)
|
|
234
|
+
this.run = null;
|
|
235
|
+
return run;
|
|
236
|
+
}
|
|
237
|
+
requireRun() {
|
|
238
|
+
if (!this.run)
|
|
239
|
+
throw new Error('No run: call startRun first, or set PROXYMAIL_QA_RUN.');
|
|
240
|
+
return this.run;
|
|
241
|
+
}
|
|
190
242
|
async wait(address, kind, options, type = '', app) {
|
|
191
243
|
const total = options.timeout ?? MAX_WAIT_SECONDS;
|
|
192
244
|
const deadline = Date.now() + total * 1000;
|
|
@@ -202,8 +254,9 @@ export class ProxyMailQA {
|
|
|
202
254
|
query.set('type', type);
|
|
203
255
|
if (app !== undefined)
|
|
204
256
|
query.set('app', String(app));
|
|
257
|
+
const more = Date.now() + left * 1000 < deadline - 500;
|
|
205
258
|
try {
|
|
206
|
-
return await this.request('GET', `/addresses/${encodeURIComponent(address)}/${kind}?${query}
|
|
259
|
+
return await this.request('GET', `/addresses/${encodeURIComponent(address)}/${kind}?${query}`, undefined, more);
|
|
207
260
|
}
|
|
208
261
|
catch (error) {
|
|
209
262
|
const timedOut = error instanceof ProxyMailQAError && error.status === 404 && TIMED_OUT.test(error.message);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "proxymail-qa",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Disposable test inboxes for Playwright, Cypress and scripts: create an address, then wait for its verification code or link.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Proxy Mail QA <hello@proxymail.qa> (https://proxymail.qa)",
|