@bigbinary/neeto-playwright-commons 2.2.0 → 2.2.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/index.cjs.js +122 -84
- package/index.cjs.js.map +1 -1
- package/index.d.ts +29 -16
- package/index.js +121 -84
- package/index.js.map +1 -1
- package/package.json +1 -1
package/index.cjs.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var child_process = require('child_process');
|
|
4
3
|
var neetoCist = require('@bigbinary/neeto-cist');
|
|
5
4
|
var faker = require('@faker-js/faker');
|
|
6
5
|
var fs$4 = require('fs');
|
|
@@ -10,6 +9,7 @@ var test = require('@playwright/test');
|
|
|
10
9
|
var playwrightI18nextFixture = require('playwright-i18next-fixture');
|
|
11
10
|
var require$$0$3 = require('util');
|
|
12
11
|
var ramda = require('ramda');
|
|
12
|
+
var child_process = require('child_process');
|
|
13
13
|
var dayjs = require('dayjs');
|
|
14
14
|
var require$$0$7 = require('stream');
|
|
15
15
|
var require$$0$6 = require('node:buffer');
|
|
@@ -247,86 +247,63 @@ class MemberApis {
|
|
|
247
247
|
});
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
class
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
250
|
+
class RailsEmailApiClient {
|
|
251
|
+
port = process.env.RAILS_SERVER_PORT;
|
|
252
|
+
subdomain = process.env.SUBDOMAIN ?? "spinkart";
|
|
253
|
+
baseUrl = `http://${this.subdomain}.lvh.me:${this.port}`;
|
|
254
|
+
emailsEndpoint = `${this.baseUrl}/api/v1/testing/emails`;
|
|
255
|
+
constructor() { }
|
|
256
|
+
convertRawEmail = (raw) => ({
|
|
257
|
+
from: raw.from,
|
|
258
|
+
to: raw.to,
|
|
259
|
+
cc: raw.cc,
|
|
260
|
+
bcc: raw.bcc,
|
|
261
|
+
replyTo: raw.reply_to,
|
|
262
|
+
subject: raw.subject,
|
|
263
|
+
htmlBody: raw.html_body,
|
|
264
|
+
textBody: raw.text_body,
|
|
265
|
+
receivedAt: raw.received_at,
|
|
266
|
+
attachments: raw.attachments?.map(({ filename, mime_type, data }) => ({
|
|
267
|
+
name: filename,
|
|
268
|
+
type: mime_type,
|
|
269
|
+
content: data,
|
|
269
270
|
})),
|
|
270
271
|
});
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
stdout += data.toString();
|
|
280
|
-
});
|
|
281
|
-
childProcess.stderr?.on("data", data => {
|
|
282
|
-
stderr += data.toString();
|
|
283
|
-
});
|
|
284
|
-
const exitCode = await new Promise((resolve, reject) => {
|
|
285
|
-
childProcess.on("error", reject);
|
|
286
|
-
childProcess.on("close", resolve);
|
|
287
|
-
});
|
|
288
|
-
if (exitCode !== 0) {
|
|
289
|
-
throw new Error(`Rake task ${taskName} failed: ${stderr || stdout || `Exit code ${exitCode}`}`);
|
|
290
|
-
}
|
|
291
|
-
return this.extractJsonFromOutput(stdout);
|
|
292
|
-
};
|
|
293
|
-
extractJsonFromOutput = (output) => {
|
|
294
|
-
const delimiterMatch = output.match(/<-- Captured Emails Start-->([\s\S]*?)<-- Captured Emails End-->/);
|
|
295
|
-
return delimiterMatch ? delimiterMatch[1].trim() : output.trim();
|
|
272
|
+
buildQueryString = (params) => {
|
|
273
|
+
if (!params)
|
|
274
|
+
return "";
|
|
275
|
+
const filtered = Object.fromEntries(Object.entries(neetoCist.keysToSnakeCase(params))
|
|
276
|
+
.filter(([, v]) => v != null && v !== "")
|
|
277
|
+
.map(([k, v]) => [k, String(v)]));
|
|
278
|
+
const query = new URLSearchParams(filtered).toString();
|
|
279
|
+
return query ? `?${query}` : "";
|
|
296
280
|
};
|
|
297
|
-
|
|
281
|
+
fetchJson = async (url, options) => {
|
|
298
282
|
try {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const rawEmails = JSON.parse(output);
|
|
304
|
-
return rawEmails.map(this.convertRawEmail);
|
|
283
|
+
const res = await fetch(url, options);
|
|
284
|
+
if (!res.ok)
|
|
285
|
+
return null;
|
|
286
|
+
return (await res.json());
|
|
305
287
|
}
|
|
306
|
-
catch (
|
|
307
|
-
console.error("
|
|
308
|
-
return
|
|
288
|
+
catch (err) {
|
|
289
|
+
console.error("API error:", err);
|
|
290
|
+
return null;
|
|
309
291
|
}
|
|
310
292
|
};
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
293
|
+
listEmails = async (searchParams) => {
|
|
294
|
+
const query = this.buildQueryString(searchParams);
|
|
295
|
+
const data = await this.fetchJson(`${this.emailsEndpoint}${query}`);
|
|
296
|
+
return data?.map(this.convertRawEmail) ?? [];
|
|
297
|
+
};
|
|
314
298
|
getLatestEmail = async (searchParams) => {
|
|
315
299
|
const emails = await this.listEmails(searchParams);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
? current
|
|
320
|
-
: latest);
|
|
321
|
-
};
|
|
322
|
-
clearEmails = async () => {
|
|
323
|
-
try {
|
|
324
|
-
await this.executeRakeTask("playwright:clear_captured_emails");
|
|
325
|
-
}
|
|
326
|
-
catch (error) {
|
|
327
|
-
console.error("Failed to clear emails:", error);
|
|
328
|
-
}
|
|
300
|
+
return emails.reduce((latest, curr) => !latest || new Date(curr.receivedAt) > new Date(latest.receivedAt)
|
|
301
|
+
? curr
|
|
302
|
+
: latest, null);
|
|
329
303
|
};
|
|
304
|
+
clearEmails = () => this.fetchJson(this.emailsEndpoint, {
|
|
305
|
+
method: "DELETE",
|
|
306
|
+
});
|
|
330
307
|
}
|
|
331
308
|
|
|
332
309
|
class RoleApis {
|
|
@@ -5456,6 +5433,7 @@ const LOGIN_SELECTORS = {
|
|
|
5456
5433
|
twitterAuthenticationButton: "twitter-authentication-button",
|
|
5457
5434
|
authenticatorAppOtpInput: "authenticator-app-otp-login-code-input",
|
|
5458
5435
|
enterOtpInputError: "enter-otp-input-error",
|
|
5436
|
+
authenticatorAppLaterButton: "authenticator-app-promo-later-button",
|
|
5459
5437
|
};
|
|
5460
5438
|
|
|
5461
5439
|
const MEMBER_SELECTORS = {
|
|
@@ -8584,6 +8562,10 @@ class OrganizationPage {
|
|
|
8584
8562
|
await submitButton.click();
|
|
8585
8563
|
await test.expect(submitButton).toBeHidden({ timeout: 35_000 });
|
|
8586
8564
|
};
|
|
8565
|
+
dismissAuthenticatorSetupPromptIfPresent = async () => {
|
|
8566
|
+
const maybeLaterButton = this.page.getByTestId(LOGIN_SELECTORS.authenticatorAppLaterButton);
|
|
8567
|
+
(await maybeLaterButton.isVisible()) && (await maybeLaterButton.click());
|
|
8568
|
+
};
|
|
8587
8569
|
createOrganization = async ({ email, businessName, subdomainName, firstName, lastName, appName, }) => {
|
|
8588
8570
|
if (shouldSkipSetupAndTeardown())
|
|
8589
8571
|
return;
|
|
@@ -8727,11 +8709,13 @@ class OrganizationPage {
|
|
|
8727
8709
|
}
|
|
8728
8710
|
await this.fillEmailAndSubmit(email, loginTimeout);
|
|
8729
8711
|
await this.fillOTP();
|
|
8712
|
+
await this.dismissAuthenticatorSetupPromptIfPresent();
|
|
8730
8713
|
};
|
|
8731
8714
|
loginWithFastmailEmail = async ({ email, loginTimeout = 2 * 60 * 1000, fetchOtpFromEmail, }) => {
|
|
8732
8715
|
await this.fillEmailAndSubmit(email, loginTimeout);
|
|
8733
8716
|
const otp = await fetchOtpFromEmail({ email, timeout: 4 * 60 * 1000 });
|
|
8734
8717
|
await this.fillOTP(otp);
|
|
8718
|
+
await this.dismissAuthenticatorSetupPromptIfPresent();
|
|
8735
8719
|
};
|
|
8736
8720
|
setupProfile = async ({ firstName = faker.faker.person.firstName(), lastName = faker.faker.person.lastName(), country, } = {}) => {
|
|
8737
8721
|
if (IS_DEV_ENV)
|
|
@@ -60309,10 +60293,10 @@ const hexToRGB = (hex) => {
|
|
|
60309
60293
|
|
|
60310
60294
|
class RailsEmailUtils {
|
|
60311
60295
|
neetoPlaywrightUtilities;
|
|
60312
|
-
|
|
60296
|
+
railsEmailClient;
|
|
60313
60297
|
constructor(neetoPlaywrightUtilities) {
|
|
60314
60298
|
this.neetoPlaywrightUtilities = neetoPlaywrightUtilities;
|
|
60315
|
-
this.
|
|
60299
|
+
this.railsEmailClient = new RailsEmailApiClient();
|
|
60316
60300
|
}
|
|
60317
60301
|
convertRailsEmailToFormattedList = (railsEmail) => {
|
|
60318
60302
|
if (!railsEmail)
|
|
@@ -60363,11 +60347,11 @@ class RailsEmailUtils {
|
|
|
60363
60347
|
blobId: "",
|
|
60364
60348
|
};
|
|
60365
60349
|
};
|
|
60366
|
-
clearEmails = () => this.
|
|
60367
|
-
getLatestEmail = (searchParams) => this.
|
|
60368
|
-
listEmails = (searchParams) => this.
|
|
60350
|
+
clearEmails = () => this.railsEmailClient.clearEmails();
|
|
60351
|
+
getLatestEmail = (searchParams) => this.railsEmailClient.getLatestEmail(searchParams);
|
|
60352
|
+
listEmails = (searchParams) => this.railsEmailClient.listEmails(searchParams);
|
|
60369
60353
|
listMessages = async (messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), } = {}) => {
|
|
60370
|
-
const emails = await this.
|
|
60354
|
+
const emails = await this.railsEmailClient.listEmails({
|
|
60371
60355
|
...messageSearchCriteria,
|
|
60372
60356
|
receivedAfter: receivedAfter.toISOString(),
|
|
60373
60357
|
});
|
|
@@ -60378,7 +60362,7 @@ class RailsEmailUtils {
|
|
|
60378
60362
|
findMessage = async (messageSearchCriteria = {}, { timeout = 10_000, receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
|
|
60379
60363
|
const email = (await this.neetoPlaywrightUtilities.executeRecursively({
|
|
60380
60364
|
callback: async () => {
|
|
60381
|
-
const railsEmail = await this.
|
|
60365
|
+
const railsEmail = await this.railsEmailClient.getLatestEmail({
|
|
60382
60366
|
...messageSearchCriteria,
|
|
60383
60367
|
receivedAfter: receivedAfter.toISOString(),
|
|
60384
60368
|
});
|
|
@@ -60387,7 +60371,7 @@ class RailsEmailUtils {
|
|
|
60387
60371
|
return this.convertRailsEmailToFormattedList(railsEmail);
|
|
60388
60372
|
},
|
|
60389
60373
|
condition: async () => {
|
|
60390
|
-
const emails = await this.
|
|
60374
|
+
const emails = await this.railsEmailClient.listEmails({
|
|
60391
60375
|
...messageSearchCriteria,
|
|
60392
60376
|
receivedAfter: receivedAfter.toISOString(),
|
|
60393
60377
|
});
|
|
@@ -60406,7 +60390,7 @@ class RailsEmailUtils {
|
|
|
60406
60390
|
findOtpFromEmail = async ({ email, subjectSubstring = OTP_EMAIL_PATTERN, timeout = 10_000, receivedAfter = new Date(), expectedEmailCount = 1, }) => {
|
|
60407
60391
|
const otp = await this.neetoPlaywrightUtilities.executeRecursively({
|
|
60408
60392
|
callback: async () => {
|
|
60409
|
-
const railsEmail = await this.
|
|
60393
|
+
const railsEmail = await this.railsEmailClient.getLatestEmail({
|
|
60410
60394
|
to: email,
|
|
60411
60395
|
subject: subjectSubstring,
|
|
60412
60396
|
receivedAfter: receivedAfter.toISOString(),
|
|
@@ -60419,7 +60403,7 @@ class RailsEmailUtils {
|
|
|
60419
60403
|
return formattedEmail.html.codes?.[0] || formattedEmail.text.codes?.[0];
|
|
60420
60404
|
},
|
|
60421
60405
|
condition: async () => {
|
|
60422
|
-
const emails = await this.
|
|
60406
|
+
const emails = await this.railsEmailClient.listEmails({
|
|
60423
60407
|
to: email,
|
|
60424
60408
|
subject: subjectSubstring,
|
|
60425
60409
|
receivedAfter: receivedAfter.toISOString(),
|
|
@@ -60433,7 +60417,7 @@ class RailsEmailUtils {
|
|
|
60433
60417
|
getEmailAttachment = async (attachmentName, messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, timeout = 10_000, } = {}, shouldThrowErrorOnTimeout = true) => {
|
|
60434
60418
|
const attachmentDetails = (await this.neetoPlaywrightUtilities.executeRecursively({
|
|
60435
60419
|
callback: async () => {
|
|
60436
|
-
const railsEmail = await this.
|
|
60420
|
+
const railsEmail = await this.railsEmailClient.getLatestEmail({
|
|
60437
60421
|
...messageSearchCriteria,
|
|
60438
60422
|
receivedAfter: receivedAfter.toISOString(),
|
|
60439
60423
|
});
|
|
@@ -60450,7 +60434,7 @@ class RailsEmailUtils {
|
|
|
60450
60434
|
};
|
|
60451
60435
|
},
|
|
60452
60436
|
condition: async () => {
|
|
60453
|
-
const emails = await this.
|
|
60437
|
+
const emails = await this.railsEmailClient.listEmails({
|
|
60454
60438
|
...messageSearchCriteria,
|
|
60455
60439
|
receivedAfter: receivedAfter.toISOString(),
|
|
60456
60440
|
});
|
|
@@ -107252,7 +107236,35 @@ class ColorPickerUtils {
|
|
|
107252
107236
|
};
|
|
107253
107237
|
}
|
|
107254
107238
|
|
|
107239
|
+
const STATIC_ASSET_PATTERN = /\.(js|css|woff2?|ttf|eot|png|svg|ico|gif|webp)(\?.*)?$/;
|
|
107240
|
+
const assetCache = new Map();
|
|
107255
107241
|
const commands = {
|
|
107242
|
+
context: async ({ context }, use) => {
|
|
107243
|
+
if (IS_DEV_ENV) {
|
|
107244
|
+
await context.route(STATIC_ASSET_PATTERN, async (route) => {
|
|
107245
|
+
const url = route.request().url();
|
|
107246
|
+
const hit = assetCache.get(url);
|
|
107247
|
+
if (hit)
|
|
107248
|
+
return route.fulfill(hit);
|
|
107249
|
+
try {
|
|
107250
|
+
const response = await route.fetch();
|
|
107251
|
+
const body = await response.body();
|
|
107252
|
+
const entry = {
|
|
107253
|
+
body,
|
|
107254
|
+
status: response.status(),
|
|
107255
|
+
headers: response.headers(),
|
|
107256
|
+
};
|
|
107257
|
+
if (response.ok())
|
|
107258
|
+
assetCache.set(url, entry);
|
|
107259
|
+
return route.fulfill(entry);
|
|
107260
|
+
}
|
|
107261
|
+
catch {
|
|
107262
|
+
return route.continue();
|
|
107263
|
+
}
|
|
107264
|
+
});
|
|
107265
|
+
}
|
|
107266
|
+
await use(context);
|
|
107267
|
+
},
|
|
107256
107268
|
neetoPlaywrightUtilities: async ({ page, request, baseURL }, use) => {
|
|
107257
107269
|
const commands = new CustomCommands(page, request, baseURL);
|
|
107258
107270
|
await use(commands);
|
|
@@ -124970,6 +124982,31 @@ const generatePhoneNumberDetails = () => {
|
|
|
124970
124982
|
return { flag: country.flag, name: country.name, code: country.code, number };
|
|
124971
124983
|
};
|
|
124972
124984
|
|
|
124985
|
+
const DEFAULT_WARMUP_URLS = ["/login"];
|
|
124986
|
+
async function warmup({ urls = DEFAULT_WARMUP_URLS, timeout = 60_000, } = {}) {
|
|
124987
|
+
if (!IS_DEV_ENV)
|
|
124988
|
+
return;
|
|
124989
|
+
const { RAILS_SERVER_PORT, SUBDOMAIN = "spinkart" } = process.env;
|
|
124990
|
+
if (!RAILS_SERVER_PORT) {
|
|
124991
|
+
throw new Error("RAILS_SERVER_PORT is not defined in environment variables.");
|
|
124992
|
+
}
|
|
124993
|
+
const baseURL = `http://${SUBDOMAIN}.lvh.me:${RAILS_SERVER_PORT}`;
|
|
124994
|
+
const browser = await test.chromium.launch();
|
|
124995
|
+
const page = await browser.newPage();
|
|
124996
|
+
try {
|
|
124997
|
+
for (const url of urls) {
|
|
124998
|
+
const fullUrl = url.startsWith("http") ? url : `${baseURL}${url}`;
|
|
124999
|
+
await page.goto(fullUrl, {
|
|
125000
|
+
waitUntil: "networkidle", // eslint-disable-line playwright/no-networkidle
|
|
125001
|
+
timeout,
|
|
125002
|
+
});
|
|
125003
|
+
}
|
|
125004
|
+
}
|
|
125005
|
+
finally {
|
|
125006
|
+
await browser.close();
|
|
125007
|
+
}
|
|
125008
|
+
}
|
|
125009
|
+
|
|
124973
125010
|
const CONFIG = {
|
|
124974
125011
|
DIR: "/tmp/neeto-auth-web",
|
|
124975
125012
|
LOG: "/tmp/neeto-auth-server.log",
|
|
@@ -125985,7 +126022,7 @@ exports.PROJECT_NAMES = PROJECT_NAMES;
|
|
|
125985
126022
|
exports.PROJECT_TRANSLATIONS_PATH = PROJECT_TRANSLATIONS_PATH;
|
|
125986
126023
|
exports.ROLES_SELECTORS = ROLES_SELECTORS;
|
|
125987
126024
|
exports.ROUTES = ROUTES;
|
|
125988
|
-
exports.
|
|
126025
|
+
exports.RailsEmailApiClient = RailsEmailApiClient;
|
|
125989
126026
|
exports.RailsEmailUtils = RailsEmailUtils;
|
|
125990
126027
|
exports.RoleApis = RoleApis;
|
|
125991
126028
|
exports.RolesPage = RolesPage;
|
|
@@ -126087,5 +126124,6 @@ exports.stealthTest = stealth;
|
|
|
126087
126124
|
exports.tableUtils = tableUtils;
|
|
126088
126125
|
exports.toCamelCase = toCamelCase;
|
|
126089
126126
|
exports.updateCredentials = updateCredentials;
|
|
126127
|
+
exports.warmup = warmup;
|
|
126090
126128
|
exports.writeDataToFile = writeDataToFile;
|
|
126091
126129
|
//# sourceMappingURL=index.cjs.js.map
|