@bigbinary/neeto-playwright-commons 3.2.0 → 3.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.d.ts CHANGED
@@ -31,6 +31,7 @@ interface ExecuteRecursivelyParams {
31
31
  interface VerifyToastParams {
32
32
  message: string;
33
33
  timeout?: number;
34
+ toastCloseTimeout?: number;
34
35
  closeAfterVerification: boolean;
35
36
  toastType: "success" | "error";
36
37
  customPageContext?: Page;
@@ -219,6 +220,8 @@ declare class CustomCommands {
219
220
  *
220
221
  * timeout(optional): Timeout in milliseconds to wait for the toast to appear. Default is 10_000.
221
222
  *
223
+ * toastCloseTimeout(optional): Timeout in milliseconds to wait for the toast to disappear after closing. Default is 10_000.
224
+ *
222
225
  * customPageContext(optional): Custom page context on which to verify the toast.
223
226
  *
224
227
  * @example
@@ -228,6 +231,7 @@ declare class CustomCommands {
228
231
  * closeAfterVerification: false,
229
232
  * toastType: "error",
230
233
  * timeout: 20_000,
234
+ * toastCloseTimeout: 15_000,
231
235
  * customPageContext: this.page,
232
236
  * });
233
237
  * @endexample
@@ -237,6 +241,7 @@ declare class CustomCommands {
237
241
  toastType,
238
242
  closeAfterVerification,
239
243
  timeout,
244
+ toastCloseTimeout,
240
245
  customPageContext
241
246
  }?: Partial<VerifyToastParams>) => Promise<void>;
242
247
  /**
@@ -998,6 +1003,8 @@ declare class SlackApi {
998
1003
  fetchMessages: (channelId: string, unixTimestamp: number) => Promise<playwright_core.APIResponse | undefined>;
999
1004
  createChannel: (channelName: string) => Promise<playwright_core.APIResponse | undefined>;
1000
1005
  addUser: (channelId: string, userId: string) => Promise<playwright_core.APIResponse | undefined>;
1006
+ listChannels: () => Promise<playwright_core.APIResponse | undefined>;
1007
+ archiveChannel: (channelId: string) => Promise<playwright_core.APIResponse | undefined>;
1001
1008
  mockChannelDeleted: (channelId: string) => Promise<playwright_core.APIResponse | undefined>;
1002
1009
  }
1003
1010
  declare class WebhookSiteApi {
@@ -2917,6 +2924,7 @@ declare class SlackPage extends IntegrationBase {
2917
2924
  allowButton: Locator;
2918
2925
  callbackUrl: string;
2919
2926
  currentWorkspace: string;
2927
+ slackApi: SlackApi;
2920
2928
  constructor({
2921
2929
  page,
2922
2930
  neetoPlaywrightUtilities,
@@ -3088,18 +3096,25 @@ declare class SlackPage extends IntegrationBase {
3088
3096
  channelName,
3089
3097
  kind
3090
3098
  }: CreateNewSlackChannelParams) => Promise<void>;
3099
+ /**
3100
+ * @deprecated Use {@link SlackPage.archiveChannelViaAPI} instead. This flow
3101
+ * drives the Slack web UI to permanently delete a channel and is brittle;
3102
+ * `archiveChannelViaAPI` archives via the Slack Web API (requires
3103
+ * `SLACK_BOT_TOKEN`).
3104
+ */
3105
+ deleteSlackChannel: (channel: string) => Promise<void>;
3091
3106
  /**
3092
3107
  *
3093
- * Deletes a slack channel. It takes the following parameters:
3108
+ * Archives a Slack channel by name using the Slack Web API (conversations.list then conversations.archive). Requires SLACK_BOT_TOKEN with scopes that allow listing and archiving channels. Does not drive the Slack web UI; use when tests only need the channel removed or archived on the workspace.
3094
3109
  *
3095
- * channel: Name of the Slack channel to delete.
3110
+ * channel: Channel name to archive.
3096
3111
  *
3097
3112
  * @example
3098
3113
  *
3099
- * await slackPage.deleteSlackChannel("old-channel");
3114
+ * await slackPage.archiveChannelViaAPI("temp-test-channel");
3100
3115
  * @endexample
3101
3116
  */
3102
- deleteSlackChannel: (channel: string) => Promise<void>;
3117
+ archiveChannelViaAPI: (name: string) => Promise<void>;
3103
3118
  }
3104
3119
  interface WebhooksPageParams {
3105
3120
  page: Page;
@@ -4547,8 +4562,32 @@ declare class OrganizationPage {
4547
4562
  page: Page;
4548
4563
  neetoPlaywrightUtilities: CustomCommands;
4549
4564
  constructor(page: Page, neetoPlaywrightUtilities: CustomCommands);
4550
- private fillOTP;
4551
- private submitEmail;
4565
+ /**
4566
+ *
4567
+ * Waits for the page to load, fills the OTP field, and waits until the OTP input is hidden (up to 35s), indicating the code was accepted.
4568
+ *
4569
+ * otp (optional): One-time password to enter. Defaults to a random 6-digit string.
4570
+ *
4571
+ * @example
4572
+ *
4573
+ * await organizationPage.fillOTP();
4574
+ * // OR
4575
+ * await organizationPage.fillOTP("123456");
4576
+ * @endexample
4577
+ */
4578
+ fillOTP: (otp?: string) => Promise<void>;
4579
+ /**
4580
+ *
4581
+ * Waits for the page to load, fills the signup email field, clicks submit, and waits until the submit button is hidden (up to 35s). Use this as the first step when driving the email-based signup flow before entering an OTP.
4582
+ *
4583
+ * email: Address to enter in the signup email field.
4584
+ *
4585
+ * @example
4586
+ *
4587
+ * await organizationPage.submitEmail("jane@example.com");
4588
+ * @endexample
4589
+ */
4590
+ submitEmail: (email: string) => Promise<void>;
4552
4591
  private dismissAuthenticatorSetupPromptIfPresent;
4553
4592
  /**
4554
4593
  *
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { keysToSnakeCase, isPresent, hyphenate, isNotPresent, humanize, dynamicArray, findBy, truncate, isNotEmpty, isNotEqualDeep, randomPick } from '@bigbinary/neeto-cist';
1
+ import { keysToSnakeCase, isPresent, hyphenate, isNotPresent, humanize, findBy, dynamicArray, truncate, isNotEmpty, isNotEqualDeep, randomPick } from '@bigbinary/neeto-cist';
2
2
  import { faker } from '@faker-js/faker';
3
3
  import * as fs$4 from 'fs';
4
4
  import fs__default, { readFileSync, promises, existsSync, writeFileSync, unlinkSync, mkdirSync, rmSync, createWriteStream } from 'fs';
@@ -618,6 +618,8 @@ class SlackApi {
618
618
  fetchMessages = (channelId, unixTimestamp) => this.apiRequest("history", { channel: channelId, oldest: unixTimestamp });
619
619
  createChannel = (channelName) => this.apiRequest("create", { name: channelName });
620
620
  addUser = (channelId, userId) => this.apiRequest("invite", { channel: channelId, users: userId });
621
+ listChannels = () => this.apiRequest("list", { exclude_archived: 1, limit: 1000 });
622
+ archiveChannel = (channelId) => this.apiRequest("archive", { channel: channelId });
621
623
  mockChannelDeleted = (channelId) => this.neetoPlaywrightUtilities.apiRequest({
622
624
  url: "/neeto_slack/api/v1/testing/webhooks",
623
625
  method: "post",
@@ -6048,7 +6050,7 @@ class CustomCommands {
6048
6050
  const startTime = Date.now();
6049
6051
  return await this.recursiveMethod(callback, condition, timeout, startTime, 1);
6050
6052
  };
6051
- verifyToast = async ({ message = "", toastType = "success", closeAfterVerification = true, timeout = 10_000, customPageContext = this.page, } = {}) => {
6053
+ verifyToast = async ({ message = "", toastType = "success", closeAfterVerification = true, timeout = 10_000, toastCloseTimeout = 10_000, customPageContext = this.page, } = {}) => {
6052
6054
  // React-toastify does not support adding data-* attributes to toast DOM elements: https://github.com/fkhadra/react-toastify/issues/1106
6053
6055
  const toastrLocator = customPageContext
6054
6056
  .locator(COMMON_SELECTORS.neetoUiToastr)
@@ -6069,7 +6071,9 @@ class CustomCommands {
6069
6071
  await customPageContext.evaluate(button => {
6070
6072
  button.dispatchEvent(new Event("click", { bubbles: true }));
6071
6073
  }, buttonHandle);
6072
- await expect(toastrCloseButton).toBeHidden({ timeout: 10_000 });
6074
+ await expect(toastrCloseButton).toBeHidden({
6075
+ timeout: toastCloseTimeout,
6076
+ });
6073
6077
  }
6074
6078
  };
6075
6079
  waitForPageLoad = async ({ visibilityTimeout = 35_000, customPageContext = this.page, waitForOption = "serial", } = {}) => {
@@ -119460,6 +119464,7 @@ class SlackPage extends IntegrationBase {
119460
119464
  allowButton;
119461
119465
  callbackUrl;
119462
119466
  currentWorkspace;
119467
+ slackApi;
119463
119468
  constructor({ page, neetoPlaywrightUtilities, integrationRouteIndex, }) {
119464
119469
  super({
119465
119470
  page,
@@ -119470,6 +119475,7 @@ class SlackPage extends IntegrationBase {
119470
119475
  this.allowButton = this.page.getByRole("button", {
119471
119476
  name: SLACK_WEB_TEXTS.allow,
119472
119477
  });
119478
+ this.slackApi = new SlackApi(this.neetoPlaywrightUtilities);
119473
119479
  }
119474
119480
  setupCloseHandlers = async (slackWebappPage = this.page) => {
119475
119481
  const slackWebappPageDataQa = getByDataQA(slackWebappPage);
@@ -119654,6 +119660,12 @@ class SlackPage extends IntegrationBase {
119654
119660
  await nextButton.click();
119655
119661
  await this.slackWebappPageDataQa(SLACK_DATA_QA_SELECTORS.inviteToWorkspaceSkipButton).click();
119656
119662
  };
119663
+ /**
119664
+ * @deprecated Use {@link SlackPage.archiveChannelViaAPI} instead. This flow
119665
+ * drives the Slack web UI to permanently delete a channel and is brittle;
119666
+ * `archiveChannelViaAPI` archives via the Slack Web API (requires
119667
+ * `SLACK_BOT_TOKEN`).
119668
+ */
119657
119669
  deleteSlackChannel = async (channel) => {
119658
119670
  const channelItem = this.slackWebappPage.locator(SLACK_SELECTORS.channelItems, { hasText: channel });
119659
119671
  await channelItem.click({ button: "right" });
@@ -119674,6 +119686,12 @@ class SlackPage extends IntegrationBase {
119674
119686
  .click();
119675
119687
  await expect(channelItem).toBeHidden();
119676
119688
  };
119689
+ archiveChannelViaAPI = async (name) => {
119690
+ const response = await this.slackApi.listChannels();
119691
+ const { channels } = (await response?.json());
119692
+ const channel = findBy({ name }, channels);
119693
+ channel?.id && (await this.slackApi.archiveChannel(channel.id));
119694
+ };
119677
119695
  }
119678
119696
 
119679
119697
  class WebhooksPage {
@@ -126725,6 +126743,7 @@ const definePlaywrightConfig = (overrides) => {
126725
126743
  name: PROJECT_NAMES.productionHealth,
126726
126744
  testDir: "./e2e/health",
126727
126745
  testMatch: "production.health.ts",
126746
+ retries: IS_CI ? 3 : 0,
126728
126747
  use: {
126729
126748
  ...devices["Desktop Chrome"],
126730
126749
  baseURL: process.env.PLAYWRIGHT_PRODUCTION_BASE_URL,