@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 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 RailsEmailRakeClient {
251
- workingDirectory;
252
- constructor() {
253
- this.workingDirectory = process.env.RAILS_ROOT || "..";
254
- }
255
- convertRawEmail = (rawEmail) => ({
256
- from: rawEmail.from,
257
- to: rawEmail.to,
258
- cc: rawEmail.cc,
259
- bcc: rawEmail.bcc,
260
- replyTo: rawEmail.reply_to,
261
- subject: rawEmail.subject,
262
- htmlBody: rawEmail.html_body,
263
- textBody: rawEmail.text_body,
264
- receivedAt: rawEmail.received_at,
265
- attachments: rawEmail.attachments?.map(att => ({
266
- name: att.filename,
267
- type: att.mime_type,
268
- content: att.data,
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
- executeRakeTask = async (taskName, args = []) => {
272
- const childProcess = child_process.spawn("bundle", ["exec", "rake", taskName, "--", ...args], {
273
- cwd: this.workingDirectory,
274
- stdio: ["ignore", "pipe", "pipe"],
275
- });
276
- let stdout = "";
277
- let stderr = "";
278
- childProcess.stdout?.on("data", data => {
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
- listEmails = async (searchParams) => {
281
+ fetchJson = async (url, options) => {
298
282
  try {
299
- const args = this.buildSearchArgs(searchParams);
300
- const output = await this.executeRakeTask("playwright:fetch_captured_emails", args);
301
- if (!output)
302
- return [];
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 (error) {
307
- console.error("Failed to fetch emails:", error);
308
- return [];
288
+ catch (err) {
289
+ console.error("API error:", err);
290
+ return null;
309
291
  }
310
292
  };
311
- buildSearchArgs = (searchParams) => Object.entries(neetoCist.keysToSnakeCase(searchParams ?? {}))
312
- .filter(([, value]) => value != null && value !== "")
313
- .map(([key, value]) => `--${key}=${value}`);
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
- if (emails.length === 0)
317
- return null;
318
- return emails.reduce((latest, current) => new Date(current.receivedAt) > new Date(latest.receivedAt)
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
- railsEmailRakeClient;
60296
+ railsEmailClient;
60313
60297
  constructor(neetoPlaywrightUtilities) {
60314
60298
  this.neetoPlaywrightUtilities = neetoPlaywrightUtilities;
60315
- this.railsEmailRakeClient = new RailsEmailRakeClient();
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.railsEmailRakeClient.clearEmails();
60367
- getLatestEmail = (searchParams) => this.railsEmailRakeClient.getLatestEmail(searchParams);
60368
- listEmails = (searchParams) => this.railsEmailRakeClient.listEmails(searchParams);
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.railsEmailRakeClient.listEmails({
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.railsEmailRakeClient.getLatestEmail({
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.railsEmailRakeClient.listEmails({
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.railsEmailRakeClient.getLatestEmail({
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.railsEmailRakeClient.listEmails({
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.railsEmailRakeClient.getLatestEmail({
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.railsEmailRakeClient.listEmails({
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.RailsEmailRakeClient = RailsEmailRakeClient;
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