@bigbinary/neeto-playwright-commons 1.26.22 → 1.26.24

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,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ var child_process = require('child_process');
3
4
  var neetoCist = require('@bigbinary/neeto-cist');
4
5
  var faker = require('@faker-js/faker');
5
6
  var require$$0$4 = require('fs');
@@ -9,7 +10,6 @@ var test = require('@playwright/test');
9
10
  var playwrightI18nextFixture = require('playwright-i18next-fixture');
10
11
  var require$$0$3 = require('util');
11
12
  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 node_module = require('node:module');
@@ -221,6 +221,91 @@ class MemberApis {
221
221
  }
222
222
  }
223
223
 
224
+ class RailsEmailRakeClient {
225
+ constructor() {
226
+ this.convertRawEmail = (rawEmail) => {
227
+ var _a;
228
+ return ({
229
+ from: rawEmail.from,
230
+ to: rawEmail.to,
231
+ cc: rawEmail.cc,
232
+ bcc: rawEmail.bcc,
233
+ replyTo: rawEmail.reply_to,
234
+ subject: rawEmail.subject,
235
+ htmlBody: rawEmail.html_body,
236
+ textBody: rawEmail.text_body,
237
+ receivedAt: rawEmail.received_at,
238
+ attachments: (_a = rawEmail.attachments) === null || _a === void 0 ? void 0 : _a.map(att => ({
239
+ name: att.filename,
240
+ type: att.mime_type,
241
+ content: att.data,
242
+ })),
243
+ });
244
+ };
245
+ this.executeRakeTask = async (taskName, args = []) => {
246
+ var _a, _b;
247
+ const childProcess = child_process.spawn("bundle", ["exec", "rake", taskName, "--", ...args], {
248
+ cwd: this.workingDirectory,
249
+ stdio: ["ignore", "pipe", "pipe"],
250
+ });
251
+ let stdout = "";
252
+ let stderr = "";
253
+ (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", data => {
254
+ stdout += data.toString();
255
+ });
256
+ (_b = childProcess.stderr) === null || _b === void 0 ? void 0 : _b.on("data", data => {
257
+ stderr += data.toString();
258
+ });
259
+ const exitCode = await new Promise((resolve, reject) => {
260
+ childProcess.on("error", reject);
261
+ childProcess.on("close", resolve);
262
+ });
263
+ if (exitCode !== 0) {
264
+ throw new Error(`Rake task ${taskName} failed: ${stderr || stdout || `Exit code ${exitCode}`}`);
265
+ }
266
+ return this.extractJsonFromOutput(stdout);
267
+ };
268
+ this.extractJsonFromOutput = (output) => {
269
+ const delimiterMatch = output.match(/<-- Captured Emails Start-->([\s\S]*?)<-- Captured Emails End-->/);
270
+ return delimiterMatch ? delimiterMatch[1].trim() : output.trim();
271
+ };
272
+ this.listEmails = async (searchParams) => {
273
+ try {
274
+ const args = this.buildSearchArgs(searchParams);
275
+ const output = await this.executeRakeTask("playwright:fetch_captured_emails", args);
276
+ if (!output)
277
+ return [];
278
+ const rawEmails = JSON.parse(output);
279
+ return rawEmails.map(this.convertRawEmail);
280
+ }
281
+ catch (error) {
282
+ console.error("Failed to fetch emails:", error);
283
+ return [];
284
+ }
285
+ };
286
+ this.buildSearchArgs = (searchParams) => Object.entries(neetoCist.keysToSnakeCase(searchParams !== null && searchParams !== void 0 ? searchParams : {}))
287
+ .filter(([, value]) => value != null && value !== "")
288
+ .map(([key, value]) => `--${key}=${value}`);
289
+ this.getLatestEmail = async (searchParams) => {
290
+ const emails = await this.listEmails(searchParams);
291
+ if (emails.length === 0)
292
+ return null;
293
+ return emails.reduce((latest, current) => new Date(current.receivedAt) > new Date(latest.receivedAt)
294
+ ? current
295
+ : latest);
296
+ };
297
+ this.clearEmails = async () => {
298
+ try {
299
+ await this.executeRakeTask("playwright:clear_captured_emails");
300
+ }
301
+ catch (error) {
302
+ console.error("Failed to clear emails:", error);
303
+ }
304
+ };
305
+ this.workingDirectory = process.env.RAILS_ROOT || "..";
306
+ }
307
+ }
308
+
224
309
  class RoleApis {
225
310
  constructor(neetoPlaywrightUtilities) {
226
311
  this.neetoPlaywrightUtilities = neetoPlaywrightUtilities;
@@ -57361,6 +57446,168 @@ const hexToRGB = (hex) => {
57361
57446
  return `rgb(${Number(r)}, ${Number(g)}, ${Number(b)})`;
57362
57447
  };
57363
57448
 
57449
+ class RailsEmailUtils {
57450
+ constructor(neetoPlaywrightUtilities) {
57451
+ this.neetoPlaywrightUtilities = neetoPlaywrightUtilities;
57452
+ this.convertRailsEmailToFormattedList = (railsEmail) => {
57453
+ var _a;
57454
+ if (!railsEmail)
57455
+ return null;
57456
+ const LINK_REGEX = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])/g;
57457
+ const CODE_REGEX = /(?<![#/])\b\d{4,}\b/g;
57458
+ const htmlBody = railsEmail.htmlBody || "";
57459
+ const textBody = railsEmail.textBody || "";
57460
+ const htmlBodyWithStrippedHead = htmlBody.split("</head>").at(-1) || htmlBody;
57461
+ const extractMatches = (text, regex) => text.match(regex) || null;
57462
+ const parseEmailAddress = (address) => {
57463
+ if (!address)
57464
+ return [];
57465
+ const addresses = Array.isArray(address) ? address : [address];
57466
+ return addresses.map(addr => {
57467
+ const match = addr.match(/^(.+?)\s*<(.+?)>$|^(.+)$/);
57468
+ return match
57469
+ ? {
57470
+ name: match[1] || match[3] || "",
57471
+ email: match[2] || match[3] || addr,
57472
+ }
57473
+ : { name: "", email: addr };
57474
+ });
57475
+ };
57476
+ return {
57477
+ html: {
57478
+ body: htmlBody,
57479
+ links: extractMatches(htmlBodyWithStrippedHead, LINK_REGEX),
57480
+ codes: extractMatches(htmlBodyWithStrippedHead, CODE_REGEX),
57481
+ },
57482
+ text: {
57483
+ body: textBody,
57484
+ links: extractMatches(textBody, LINK_REGEX),
57485
+ codes: extractMatches(textBody, CODE_REGEX),
57486
+ },
57487
+ id: `${railsEmail.receivedAt}-${Math.random().toString(36).substring(2, 9)}`,
57488
+ from: parseEmailAddress(railsEmail.from),
57489
+ to: parseEmailAddress(railsEmail.to),
57490
+ cc: parseEmailAddress(railsEmail.cc),
57491
+ bcc: parseEmailAddress(railsEmail.bcc),
57492
+ replyTo: parseEmailAddress(railsEmail.replyTo),
57493
+ received: new Date(railsEmail.receivedAt).toISOString(),
57494
+ subject: railsEmail.subject,
57495
+ attachments: ((_a = railsEmail.attachments) === null || _a === void 0 ? void 0 : _a.map(att => ({
57496
+ name: att.name,
57497
+ type: att.type,
57498
+ }))) || [],
57499
+ blobId: "",
57500
+ };
57501
+ };
57502
+ this.clearEmails = () => this.railsEmailRakeClient.clearEmails();
57503
+ this.getLatestEmail = (searchParams) => this.railsEmailRakeClient.getLatestEmail(searchParams);
57504
+ this.listEmails = (searchParams) => this.railsEmailRakeClient.listEmails(searchParams);
57505
+ this.listMessages = async (messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), } = {}) => {
57506
+ const emails = await this.railsEmailRakeClient.listEmails({
57507
+ ...messageSearchCriteria,
57508
+ receivedAfter: receivedAfter.toISOString(),
57509
+ });
57510
+ return emails
57511
+ .map(email => this.convertRailsEmailToFormattedList(email))
57512
+ .filter((email) => email !== null);
57513
+ };
57514
+ this.findMessage = async (messageSearchCriteria = {}, { timeout = 2 * 60 * 1000, receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
57515
+ const email = (await this.neetoPlaywrightUtilities.executeRecursively({
57516
+ callback: async () => {
57517
+ const railsEmail = await this.railsEmailRakeClient.getLatestEmail({
57518
+ ...messageSearchCriteria,
57519
+ receivedAfter: receivedAfter.toISOString(),
57520
+ });
57521
+ if (!railsEmail)
57522
+ return null;
57523
+ return this.convertRailsEmailToFormattedList(railsEmail);
57524
+ },
57525
+ condition: async () => {
57526
+ const emails = await this.railsEmailRakeClient.listEmails({
57527
+ ...messageSearchCriteria,
57528
+ receivedAfter: receivedAfter.toISOString(),
57529
+ });
57530
+ return emails.length >= expectedEmailCount;
57531
+ },
57532
+ timeout,
57533
+ }));
57534
+ if (!email) {
57535
+ if (shouldThrowErrorOnTimeout) {
57536
+ throw new Error("Timed out waiting for matching message");
57537
+ }
57538
+ return {};
57539
+ }
57540
+ return email;
57541
+ };
57542
+ this.findOtpFromEmail = async ({ email, subjectSubstring = OTP_EMAIL_PATTERN, timeout = 2 * 60 * 1000, receivedAfter = new Date(), expectedEmailCount = 1, }) => {
57543
+ const otp = await this.neetoPlaywrightUtilities.executeRecursively({
57544
+ callback: async () => {
57545
+ var _a, _b;
57546
+ const railsEmail = await this.railsEmailRakeClient.getLatestEmail({
57547
+ to: email,
57548
+ subject: subjectSubstring,
57549
+ receivedAfter: receivedAfter.toISOString(),
57550
+ });
57551
+ if (!railsEmail)
57552
+ return null;
57553
+ const formattedEmail = this.convertRailsEmailToFormattedList(railsEmail);
57554
+ if (!formattedEmail)
57555
+ return null;
57556
+ return ((_a = formattedEmail.html.codes) === null || _a === void 0 ? void 0 : _a[0]) || ((_b = formattedEmail.text.codes) === null || _b === void 0 ? void 0 : _b[0]);
57557
+ },
57558
+ condition: async () => {
57559
+ const emails = await this.railsEmailRakeClient.listEmails({
57560
+ to: email,
57561
+ subject: subjectSubstring,
57562
+ receivedAfter: receivedAfter.toISOString(),
57563
+ });
57564
+ return emails.length >= expectedEmailCount;
57565
+ },
57566
+ timeout,
57567
+ });
57568
+ return otp || undefined;
57569
+ };
57570
+ this.getEmailAttachment = async (attachmentName, messageSearchCriteria = {}, { receivedAfter = new Date(new Date().valueOf() - 60 * 60 * 1000), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
57571
+ const attachmentDetails = (await this.neetoPlaywrightUtilities.executeRecursively({
57572
+ callback: async () => {
57573
+ var _a;
57574
+ const railsEmail = await this.railsEmailRakeClient.getLatestEmail({
57575
+ ...messageSearchCriteria,
57576
+ receivedAfter: receivedAfter.toISOString(),
57577
+ });
57578
+ if (!railsEmail)
57579
+ return null;
57580
+ const attachment = (_a = railsEmail.attachments) === null || _a === void 0 ? void 0 : _a.find(att => att.name.includes(attachmentName));
57581
+ if (!attachment)
57582
+ return null;
57583
+ return {
57584
+ filename: attachment.name,
57585
+ type: attachment.type,
57586
+ content: Buffer.from(attachment.content, "base64"),
57587
+ contentType: attachment.type,
57588
+ };
57589
+ },
57590
+ condition: async () => {
57591
+ const emails = await this.railsEmailRakeClient.listEmails({
57592
+ ...messageSearchCriteria,
57593
+ receivedAfter: receivedAfter.toISOString(),
57594
+ });
57595
+ return emails.length >= expectedEmailCount;
57596
+ },
57597
+ timeout: 2 * 60 * 1000,
57598
+ }));
57599
+ if (!attachmentDetails) {
57600
+ if (shouldThrowErrorOnTimeout) {
57601
+ throw new Error("Timed out waiting for matching message or attachment not found");
57602
+ }
57603
+ return undefined;
57604
+ }
57605
+ return attachmentDetails;
57606
+ };
57607
+ this.railsEmailRakeClient = new RailsEmailRakeClient();
57608
+ }
57609
+ }
57610
+
57364
57611
  const dateTimeOneHourAgo = () => new Date(new Date().valueOf() - 60 * 60 * 1000);
57365
57612
  class MailerUtils {
57366
57613
  constructor(neetoPlaywrightUtilities) {
@@ -57404,8 +57651,10 @@ class MailerUtils {
57404
57651
  const emailBodyWithStrippedHead = emailBody.split("</head>").at(-1);
57405
57652
  const links = emailBodyWithStrippedHead.match(LINK_REGEX);
57406
57653
  const codes = emailBodyWithStrippedHead.match(CODE_REGEX);
57654
+ // Remove first and last links as Fastmail adds dot image links
57655
+ const filteredLinks = links && links.length > 2 ? links.slice(1, -1) : links;
57407
57656
  const contentRecognitions = {
57408
- links: links && [...links],
57657
+ links: filteredLinks,
57409
57658
  codes: codes && [...codes],
57410
57659
  };
57411
57660
  const html = { body: emailBody, ...contentRecognitions };
@@ -57433,6 +57682,9 @@ class MailerUtils {
57433
57682
  return formattedList;
57434
57683
  };
57435
57684
  this.listMessages = async (messageSearchCriteria = {}, listMessagesFilterCriteria = {}) => {
57685
+ if (IS_DEV_ENV) {
57686
+ return this.railsEmailUtils.listMessages(messageSearchCriteria, listMessagesFilterCriteria);
57687
+ }
57436
57688
  const { ids } = await this.queryEmail(messageSearchCriteria, listMessagesFilterCriteria);
57437
57689
  return this.getEmails(ids);
57438
57690
  };
@@ -57460,12 +57712,24 @@ class MailerUtils {
57460
57712
  return ids;
57461
57713
  };
57462
57714
  this.findMessage = async (messageSearchCriteria = {}, { timeout = 10000, receivedAfter = dateTimeOneHourAgo(), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
57715
+ if (IS_DEV_ENV) {
57716
+ return this.railsEmailUtils.findMessage(messageSearchCriteria, { receivedAfter, timeout, expectedEmailCount }, shouldThrowErrorOnTimeout);
57717
+ }
57463
57718
  const ids = await this.getEmailIds(messageSearchCriteria, { expectedEmailCount, receivedAfter, timeout }, shouldThrowErrorOnTimeout);
57464
57719
  const emails = await this.getEmails(ids);
57465
57720
  const filteredEmails = emails.filter(email => this.matchesCriteria(email, messageSearchCriteria));
57466
57721
  return filteredEmails[0] || {};
57467
57722
  };
57468
57723
  this.findOtpFromEmail = async ({ email, subjectSubstring = OTP_EMAIL_PATTERN, timeout = 2 * 60 * 1000, receivedAfter = new Date(), expectedEmailCount = 1, }) => {
57724
+ if (IS_DEV_ENV) {
57725
+ return this.railsEmailUtils.findOtpFromEmail({
57726
+ email,
57727
+ subjectSubstring,
57728
+ timeout,
57729
+ receivedAfter,
57730
+ expectedEmailCount,
57731
+ });
57732
+ }
57469
57733
  if (!this.accountId) {
57470
57734
  await this.fastmailApi.authorizeAndSetAccountId();
57471
57735
  }
@@ -57474,6 +57738,9 @@ class MailerUtils {
57474
57738
  };
57475
57739
  this.generateRandomEmail = () => faker.faker.internet.email({ provider: process.env.FASTMAIL_DOMAIN_NAME });
57476
57740
  this.getEmailAttachment = async (attachmentName, messageSearchCriteria = {}, { timeout = 10000, receivedAfter = dateTimeOneHourAgo(), expectedEmailCount = 1, } = {}, shouldThrowErrorOnTimeout = true) => {
57741
+ if (IS_DEV_ENV) {
57742
+ return this.railsEmailUtils.getEmailAttachment(attachmentName, messageSearchCriteria, { receivedAfter, expectedEmailCount }, shouldThrowErrorOnTimeout);
57743
+ }
57477
57744
  const { blobId, attachments: attachmentNameAndTypes } = await this.findMessage(messageSearchCriteria, { expectedEmailCount, receivedAfter, timeout }, shouldThrowErrorOnTimeout);
57478
57745
  const attachment = attachmentNameAndTypes.find(attachment => attachment.name.includes(attachmentName));
57479
57746
  if (!attachment)
@@ -57485,6 +57752,7 @@ class MailerUtils {
57485
57752
  return attachments.find(attachment => attachment.filename.includes(attachmentName));
57486
57753
  };
57487
57754
  this.fastmailApi = new FastmailApi(neetoPlaywrightUtilities);
57755
+ this.railsEmailUtils = new RailsEmailUtils(neetoPlaywrightUtilities);
57488
57756
  }
57489
57757
  matchesCriteria(email, criteria) {
57490
57758
  const { to, from, subject, body } = criteria;
@@ -124836,9 +125104,6 @@ const definePlaywrightConfig = (overrides) => {
124836
125104
  timezoneId: "Asia/Calcutta",
124837
125105
  geolocation: { latitude: 18.553187, longitude: 73.948313 }, // BB Pune office
124838
125106
  permissions: ["geolocation"],
124839
- launchOptions: {
124840
- args: ["--js-flags=--max-old-space-size=6144"],
124841
- },
124842
125107
  ...useOverrides,
124843
125108
  },
124844
125109
  projects: useCustomProjects
@@ -124854,6 +125119,9 @@ const definePlaywrightConfig = (overrides) => {
124854
125119
  use: {
124855
125120
  ...test.devices["Desktop Chrome"],
124856
125121
  storageState: STORAGE_STATE,
125122
+ launchOptions: {
125123
+ args: ["--js-flags=--max-old-space-size=6144"],
125124
+ },
124857
125125
  },
124858
125126
  dependencies: ["setup"],
124859
125127
  },
@@ -124972,6 +125240,8 @@ exports.PROFILE_SECTION_SELECTORS = PROFILE_SECTION_SELECTORS;
124972
125240
  exports.PROJECT_TRANSLATIONS_PATH = PROJECT_TRANSLATIONS_PATH;
124973
125241
  exports.ROLES_SELECTORS = ROLES_SELECTORS;
124974
125242
  exports.ROUTES = ROUTES;
125243
+ exports.RailsEmailRakeClient = RailsEmailRakeClient;
125244
+ exports.RailsEmailUtils = RailsEmailUtils;
124975
125245
  exports.RoleApis = RoleApis;
124976
125246
  exports.RolesPage = RolesPage;
124977
125247
  exports.SIGNUP_SELECTORS = SIGNUP_SELECTORS;