From e5da59360e036a9ab02db0acbd5308966155a863 Mon Sep 17 00:00:00 2001 From: Ibrahim Abuznaid <135799669+ibrahim-abuznaid@users.noreply.github.com> Date: Tue, 18 Aug 2026 09:54:39 +0300 Subject: [PATCH 1/5] feat(framework): widen action classification to READ/SEARCH/WRITE/DESTRUCTIVE (#14852) Co-authored-by: Claude Opus 5 (1M context) --- .../framework/src/lib/piece-metadata.ts | 7 +++++- .../generic-piece-selector-item.tsx | 22 +++++++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/pieces/framework/src/lib/piece-metadata.ts b/packages/pieces/framework/src/lib/piece-metadata.ts index 9c827cdba20d..cdf611c19195 100644 --- a/packages/pieces/framework/src/lib/piece-metadata.ts +++ b/packages/pieces/framework/src/lib/piece-metadata.ts @@ -58,9 +58,14 @@ export const AiMetadata = z.object({ }) export type AiMetadata = z.infer -export const ActionClassification = z.enum(['READ', 'WRITE']) +export const ActionClassification = z.enum(['READ', 'SEARCH', 'WRITE', 'DESTRUCTIVE']) export type ActionClassification = z.infer +export const READ_ONLY_CLASSIFICATIONS: readonly ActionClassification[] = ['READ', 'SEARCH'] + +export const isReadOnlyClassification = (classification: ActionClassification | undefined): boolean => + classification !== undefined && READ_ONLY_CLASSIFICATIONS.includes(classification) + export const PropertyGroupDisplay = z.enum(['tabs', 'section', 'summary', 'builder', 'footer']) export type PropertyGroupDisplay = z.infer diff --git a/packages/web/src/app/builder/pieces-selector/generic-piece-selector-item.tsx b/packages/web/src/app/builder/pieces-selector/generic-piece-selector-item.tsx index da377a841086..fae05d9c3961 100644 --- a/packages/web/src/app/builder/pieces-selector/generic-piece-selector-item.tsx +++ b/packages/web/src/app/builder/pieces-selector/generic-piece-selector-item.tsx @@ -1,3 +1,4 @@ +import type { ActionClassification } from '@activepieces/pieces-framework'; import { FlowActionType, FlowTriggerType } from '@activepieces/shared'; import { t } from 'i18next'; @@ -77,12 +78,15 @@ const GenericActionOrTriggerItem = ({ {pieceSelectorItemInfo.classification && ( - {pieceSelectorItemInfo.classification === 'READ' - ? t('Read') - : t('Write')} + {CLASSIFICATION_BADGE[ + pieceSelectorItemInfo.classification + ].label()} )} @@ -103,3 +107,13 @@ const GenericActionOrTriggerItem = ({ GenericActionOrTriggerItem.displayName = 'GenericActionOrTriggerItem'; export default GenericActionOrTriggerItem; + +const CLASSIFICATION_BADGE: Record< + ActionClassification, + { label: () => string; variant: 'accent' | 'destructive' } +> = { + READ: { label: () => t('Read'), variant: 'accent' }, + SEARCH: { label: () => t('Search'), variant: 'accent' }, + WRITE: { label: () => t('Write'), variant: 'accent' }, + DESTRUCTIVE: { label: () => t('Destructive'), variant: 'destructive' }, +}; From 7399223b7f312da0ec65d37a0edfa85b3ef0c66c Mon Sep 17 00:00:00 2001 From: Ibrahim Abuznaid <135799669+ibrahim-abuznaid@users.noreply.github.com> Date: Tue, 18 Aug 2026 12:24:24 +0300 Subject: [PATCH 2/5] docs: correct the Advanced-section rules for step-settings props (#14854) Co-authored-by: Claude Fable 5 --- .../skills/piece-builder/property-ui-selection.md | 14 ++++++++------ docs/build-pieces/piece-reference/properties.mdx | 12 ++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.agents/skills/piece-builder/property-ui-selection.md b/.agents/skills/piece-builder/property-ui-selection.md index e670592fd0f6..3b52000e042d 100644 --- a/.agents/skills/piece-builder/property-ui-selection.md +++ b/.agents/skills/piece-builder/property-ui-selection.md @@ -86,9 +86,9 @@ These live directly on the property. They fine-tune placement without changing t | `placeholder` | string | Any text input — show an example value (`you@example.com`). | | `width` | `'half'` | Two short related fields should sit side-by-side (First / Last name). Only takes effect **inside a `section` group**. | | `icon` | icon name | Give a filter-builder row or section field a leading glyph. Must be a **valid name** — see §5. | -| `advanced` | `true` / `false` | `false` promotes a normally-optional field into the main form (e.g. a message body). `true` pushes an important-looking field into the collapsible **Advanced** section. | +| `advanced` | `true` | Tucks a secondary field into the collapsible **Advanced** section. Nothing collapses there unless you set it — `advanced: false` is the default and does nothing. | -**Advanced section rule:** non-required props collapse into *Advanced* by default. Reach for `advanced: false` when an optional field is actually central to the action. +**Advanced section rule:** every prop renders in the main form by default, required or not. Opt a field *out* with `advanced: true`. Don't set it on a required prop — the Advanced section starts collapsed, so a mandatory field hidden there only surfaces as a validation error. --- @@ -106,13 +106,14 @@ propertyGroups: [{ key, display, label?, description?, icon?, props: ['fieldA', | Intent | `display` | Behaviour | |---|---|---| | Mutually-exclusive **modes** of the same concept (To / Cc / Bcc; by-URL vs by-ID) | `'tabs'` | Segmented control; one tab's fields visible at a time. | -| **Related fields as a titled card** (a "Send to" card, a "Message" card) | `'section'` | Titled card; `width: 'half'` packs two-up. **Keeps the Advanced section** — ungrouped optional props still collapse as usual. | +| **Related fields as a titled card** (a "Send to" card, a "Message" card) | `'section'` | Titled card; `width: 'half'` packs two-up. **Keeps the Advanced section** for props outside the cards — group members are always essential. | | A **search/filter** action where users add only the filters they need | `'builder'` | Progressive "Add filter" picker; each `builder` group is a category. A filter row persists only when its value is set — give each filter a `placeholder` + `icon`. | -| A pinned control **below** a filter builder (result limit) | `'footer'` | Pins its prop (e.g. a `stepper`) under the builder list. Pair with `'builder'` groups. | +| A pinned control **below** a filter builder (result limit) | `'footer'` | Pins its prop (e.g. a `stepper`) under the builder list. Pair with `'builder'` groups — like `'builder'`, it disables the Advanced section form-wide. | **Rules:** - Every prop named in a group must exist in `props`. -- Props left out of every group follow the normal essential/Advanced rule (only `section` preserves this — `tabs` and `builder` take full control of their members). +- Only ungrouped props honour `advanced: true` — members of `tabs` and `section` groups are always essential; the flag is ignored on them. In a sectioned layout, checkbox `reveals` targets are forced essential too: they render inline under their toggle, never in Advanced. +- **One `builder` or `footer` group disables the Advanced section for the whole action/trigger** — every prop is forced essential and `advanced: true` stops working form-wide. Don't combine a filter builder with Advanced props. - Give `section` and `builder` groups a `label` and `icon` so cards/categories read clearly. --- @@ -140,7 +141,7 @@ props: { chat_id: Property.ShortText({ displayName: 'Chat Id', required: true, placeholder: '@channel or 123456789' }), format: Property.StaticDropdown({ displayName: 'Format', required: false, display: 'cards', options: { options: [/* Markdown / HTML / Plain */] } }), message: Property.RichText({ displayName: 'Message', required: true, formatProperty: 'format' }), - disable_notification: Property.Checkbox({ displayName: 'Disable notification', required: false }), // → Advanced + disable_notification: Property.Checkbox({ displayName: 'Disable notification', required: false, advanced: true }), // → Advanced (ungrouped + flagged) }, ``` @@ -179,5 +180,6 @@ props: { - **Invalid `icon` name.** Anything outside the §5 list silently renders nothing; verify before shipping. - **`Property.Json` as an escape hatch.** If the shape is known, model it with real props or an `Array` of fields. - **`Property.DynamicProperties` for a static form.** It's the heaviest widget; only use it when fields truly depend on runtime data. +- **`advanced: true` on a required prop.** Advanced starts collapsed; a mandatory field hidden there only surfaces as a validation error. Full type syntax and dynamic-dropdown/refresher mechanics: `props-patterns.md`. Rendered previews of every option: `docs/build-pieces/piece-reference/properties.mdx`. diff --git a/docs/build-pieces/piece-reference/properties.mdx b/docs/build-pieces/piece-reference/properties.mdx index 8e91210413a3..6ee9c8430e07 100644 --- a/docs/build-pieces/piece-reference/properties.mdx +++ b/docs/build-pieces/piece-reference/properties.mdx @@ -537,9 +537,9 @@ Every property accepts a few optional hints that fine-tune how it renders. They | `placeholder` | text inputs | Grey hint text shown inside an empty field (e.g. `you@example.com`). | | `width: 'half'` | any prop inside a group | Renders two fields side-by-side instead of full-width. | | `icon` | any prop | A named icon shown beside the field in the filter builder. | -| `advanced: false` | non-required props | Forces a normally-optional field to stay **outside** the collapsible *Advanced* section. | +| `advanced: true` | any prop | Moves the field into the collapsible *Advanced* section. Props render in the main form by default. | -Non-required properties are collapsed into an **Advanced** section by default. Set `advanced: false` to promote an important optional field (like a message body) back into the main form, or `advanced: true` to push a field into Advanced. +Every property renders in the main form by default, required or not. Set `advanced: true` on a secondary option to tuck it into the collapsible **Advanced** section — `advanced: false` is the default and has no effect. Avoid the flag on required props: the section starts collapsed, so a mandatory field hidden there only surfaces as a validation error. **Half-width fields** @@ -606,12 +606,12 @@ createAction({ ``` - A filter row is shown when its value is set, so there's nothing extra to persist. Give filters short `placeholder` hints and an `icon` so each row reads clearly. + A filter row is shown when its value is set, so there's nothing extra to persist. Give filters short `placeholder` hints and an `icon` so each row reads clearly. Note that a `builder` or `footer` group also switches off the *Advanced* section for the whole step — every prop lives in the builder. ### Sectioned cards -`display: 'section'` groups related props into titled cards — for example a *Send to* card and a *Message* card. Unlike tabs and the filter builder, sectioned layouts **keep the collapsible _Advanced_ section**: any prop you don't place in a section still follows the normal essential/advanced rule, so secondary options stay tucked away. Give each group a `label` and `icon`, and use `width: 'half'` on members to pack two fields per row. +`display: 'section'` groups related props into titled cards — for example a *Send to* card and a *Message* card. Unlike tabs and the filter builder, sectioned layouts **keep the collapsible _Advanced_ section** for props outside the cards: an ungrouped prop still honours `advanced: true` — unless it is a checkbox `reveals` target, which renders inline under its toggle instead. Props inside a section are always essential. Give each group a `label` and `icon`, and use `width: 'half'` on members to pack two fields per row. @@ -626,8 +626,8 @@ createAction({ chat_id: Property.ShortText({ displayName: 'Chat Id', required: true, placeholder: '@channelusername or 123456789' }), format: Property.StaticDropdown({ displayName: 'Format', required: false, display: 'cards', options: { options: [/* Markdown / HTML / Plain */] } }), message: Property.RichText({ displayName: 'Message', required: true, formatProperty: 'format' }), - // props left out of every group collapse into Advanced as usual - disable_notification: Property.Checkbox({ displayName: 'Disable notification', required: false }), + // ungrouped props can opt into Advanced with advanced: true + disable_notification: Property.Checkbox({ displayName: 'Disable notification', required: false, advanced: true }), }, }); ``` From afc92569356a754f9c908039e4ed31f290008119 Mon Sep 17 00:00:00 2001 From: Abdul <106555838+AbdulTheActivePiecer@users.noreply.github.com> Date: Tue, 18 Aug 2026 12:32:18 +0300 Subject: [PATCH 3/5] fix(e2e): drive the new unified auth card when signing in (#14868) Co-authored-by: Claude Opus 5 (1M context) Co-authored-by: Hazem Adel Co-authored-by: Ibrahim Abuznaid <135799669+ibrahim-abuznaid@users.noreply.github.com> --- .../connections-auth/ce-authentication.md | 2 + .../engineering/e2e-tests-and-monitors.md | 28 ++++++ brain/knowledge/engineering/index.md | 1 + bun.lock | 1 + packages/tests-e2e/package.json | 1 + .../tests-e2e/pages/authentication.page.ts | 96 ++++++++++++++++++- packages/tests-e2e/pages/automations.page.ts | 3 +- ...ebhook-should-return-response.flat.spec.js | 11 ++- .../auth-landing/auth-drawer-body.tsx | 2 + 9 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 brain/knowledge/engineering/e2e-tests-and-monitors.md diff --git a/brain/knowledge/connections-auth/ce-authentication.md b/brain/knowledge/connections-auth/ce-authentication.md index add5156a971b..d6459d32c1d4 100644 --- a/brain/knowledge/connections-auth/ce-authentication.md +++ b/brain/knowledge/connections-auth/ce-authentication.md @@ -28,6 +28,8 @@ The core (all-editions) auth layer: user identity creation, sign-in, and JWT ses - **A new unauthenticated endpoint must be added to `disallowedRoutes` in `packages/web/src/lib/api.ts`**, otherwise the SPA attaches whatever stale bearer token is still in storage and the call fails in exactly the situation the endpoint exists for. - **The three signup guards in `authentication-utils.ts` differ in what they leak.** `assertEmailAuthIsEnabled` and `assertDomainIsAllowed` describe platform configuration, so surfacing their errors is safe. `assertUserIsInvitedToPlatformOrProject` describes one address, so surfacing it turns any public auth endpoint into an invitation oracle. All three are also inert unless `plan.ssoEnabled`. - **A nil `projectId` on the principal means "go to /create-platform" in four separate places.** Anything that mints a platform-less session has to satisfy all of them, not just the route guard. +- **The route no longer decides sign-in vs sign-up — the card does.** `/sign-in`, `/sign-up` and `/create-platform` all render the same `AuthLanding`; `/sign-up` is a bare redirect to `/sign-in`. Which form you get is a function of two flags: with `SMTP_CONFIGURED` the card opens on the email-code step and the classic password form exists *only* behind the "Use password" link; without it you land on a password form directly, and `USER_CREATED` picks sign-up (first ever account, no mode switch offered) over sign-in. So the same URL renders three different DOMs across Cloud, a seeded self-host, and a fresh install — anything scripting this screen has to branch, and password sign-*up* is simply unreachable once SMTP is on. +- **`/create-platform` is that same card opening on its name step**, off the ONBOARDING token rather than a route param — submitting the name is what mints the platform and project and swaps ONBOARDING for USER. A brand-new account therefore needs *two* form submissions before it has a project, which is easy to miss when automating first-run signup. ### Key files Entry point: `authenticationService`, a log-taking factory called per request from `authentication.controller.ts`, registered as `authenticationModule` in `app.ts`. diff --git a/brain/knowledge/engineering/e2e-tests-and-monitors.md b/brain/knowledge/engineering/e2e-tests-and-monitors.md new file mode 100644 index 000000000000..1ea87038f9c4 --- /dev/null +++ b/brain/knowledge/engineering/e2e-tests-and-monitors.md @@ -0,0 +1,28 @@ +--- +icon: 🎭 +--- + +# E2E Tests & Monitors + +One Playwright suite in `packages/tests-e2e` feeds three consumers that fail independently: CI on a fresh throwaway instance, Checkly monitors against **production Cloud**, and a single BetterStack monitor. A change that only breaks one of them looks green everywhere else, so it is worth knowing which reads what. + +**Local / CI suite** — `playwright.config.ts`, `testMatch: **/*.spec.ts`, split by `AP_EDITION` into `scenarios/ce` and `scenarios/ee`. Boots the whole stack itself via the `webServer` block. +**Checkly monitors** — `checkly.config.ts` picks up the *same* `**/scenarios/**/*.spec.ts` files and runs them every 10 minutes with `baseURL: https://cloud.activepieces.com`, signing in with `E2E_EMAIL` / `E2E_PASSWORD`. +**BetterStack monitor** — one standalone file, `scenarios/betterstack/*.flat.spec.js`. + +## Gotchas + +- **Checkly runs the shared specs against production Cloud**, so anything the page objects assume about the login screen has to hold on Cloud too — not just on the SMTP-less instance CI boots. This is the usual reason a UI change breaks the monitors but not CI. See the auth-card gotchas on [CE Authentication](../connections-auth/ce-authentication.md). +- **BetterStack does not read the repo — the repo pushes to it.** `.github/workflows/sync-betterstack-playwright.yml` fires on push to `main` and `PATCH`es the file's contents into the hardcoded monitor `4211060` as `playwright_script`. One-way and `main`-only: the monitor updates at *merge*, never on the PR, and any edit made in the BetterStack UI is silently overwritten by the next push. +- **The BetterStack file is deliberately flat and duplicated.** BetterStack executes one self-contained script, so it cannot `require` the `pages/` objects — its sign-in is a copy. Fix the page object and you have *not* fixed the monitor; both files need the change. +- **The `.flat.spec.js` runs nowhere else.** Playwright matches `*.spec.ts` and Checkly matches `**/scenarios/**/*.spec.ts`, so a broken flat file is invisible locally and in CI until it fails in BetterStack. +- **CI only runs on the `ready-for-e2e` label** (`e2e.yml` gates both edition workflows on it), which is why the suite can rot for weeks without anyone noticing. +- **Turbo strict env mode silently strips most of `.env.e2e`.** `globalPassThroughEnv` in `turbo.json` is an allow-list, so vars not named there never reach the `serve` tasks — verify with `tr '\0' '\n' < /proc//environ`. `AP_ENVIRONMENT` is among the casualties, so CI falls back to the `prod` default. Widening it to `AP_*` does forward them, but that alone broke worker→API Socket.IO auth (jobs queue up unconsumed), so the passthrough and the worker's `AP_WORKER_TOKEN` have to be sorted out together. +- **`AP_DEV_PIECES` loads from `packages/pieces/**/dist`, which `npm run dev` does not build.** Only pieces that happen to be build dependencies of api/worker have a `dist`, so a default dev instance serves **0 pieces** and every spec that picks a trigger times out on the piece search. Build them explicitly: `npx turbo run build --filter=@activepieces/piece-webhook --filter=@activepieces/piece-store`. +- **CE sign-up is invitation-only once a platform exists** (`INVITATION_ONLY_SIGN_UP`), so the suite's sign-up path only works on a genuinely fresh instance. Against a dev-seeded database, set `E2E_EMAIL` / `E2E_PASSWORD` instead — `global-setup.ts` prefers them and signs in rather than signing up. +- **Every workspace declares its own deps.** `@faker-js/faker` was imported by the page objects for months while only `server/api` declared it; under Bun's isolated linker that means the suite cannot import its own page objects at all. + +## Key files +- `packages/tests-e2e` — `playwright.config.ts` (local/CI), `checkly.config.ts` (Cloud monitors), `global-setup.ts` (provisions or signs in the seed account), `pages/` (shared page objects), `scenarios/betterstack/` (the standalone monitor script) +- `.github/workflows/e2e.yml` — the `ready-for-e2e` gate that calls the per-edition workflows +- `.github/workflows/sync-betterstack-playwright.yml` — the push-to-`main` upload diff --git a/brain/knowledge/engineering/index.md b/brain/knowledge/engineering/index.md index ecc04019f27f..47f707f5a294 100644 --- a/brain/knowledge/engineering/index.md +++ b/brain/knowledge/engineering/index.md @@ -33,4 +33,5 @@ The **Activepieces engineering brain**: how the system works, and *why* it was b - **Web Feature Anatomy** — the frontend feature folder, its barrel, route guards, and when a query gets the global error dialog - **Cloud Deployment Paths** — canary → prod, the `cloud-hotfix` override, and the breaking-migration gate that blocks both - **CI PR Review Hygiene** — draft-first Greptile review, the per-area PR size gate, and the workflow conventions reviewers keep re-litigating +- **E2E Tests & Monitors** — the one Playwright suite behind CI, the Checkly monitors that run it against production Cloud, and the BetterStack script the repo pushes on merge - **Architecture Spine** — the load-bearing structure of the codebase, and the gotchas that come with it: request-body `.max()` as data loss, TypeORM soft-delete across a canary window, and canary not proxying websockets diff --git a/bun.lock b/bun.lock index 677c132c410d..e0f522eb4072 100644 --- a/bun.lock +++ b/bun.lock @@ -10889,6 +10889,7 @@ "name": "tests-e2e", "version": "0.0.1", "devDependencies": { + "@faker-js/faker": "8.2.0", "@playwright/test": "1.54.1", "checkly": "6.9.10", "dotenv": "17.2.3", diff --git a/packages/tests-e2e/package.json b/packages/tests-e2e/package.json index 27ca18204ede..dc1e738d80a4 100644 --- a/packages/tests-e2e/package.json +++ b/packages/tests-e2e/package.json @@ -8,6 +8,7 @@ }, "dependencies": {}, "devDependencies": { + "@faker-js/faker": "8.2.0", "@playwright/test": "1.54.1", "checkly": "6.9.10", "dotenv": "17.2.3" diff --git a/packages/tests-e2e/pages/authentication.page.ts b/packages/tests-e2e/pages/authentication.page.ts index ac144fbd6e91..1cd447c49976 100644 --- a/packages/tests-e2e/pages/authentication.page.ts +++ b/packages/tests-e2e/pages/authentication.page.ts @@ -7,20 +7,24 @@ export class AuthenticationPage extends BasePage { async signIn(params: { email: string; password: string }) { await this.page.goto(this.url); - + await this.openPasswordForm(); + await this.switchToSignInForm(); + const emailField = this.page.getByTestId('sign-in-email'); await emailField.click(); await emailField.fill(params.email); - + const passwordField = this.page.getByTestId('sign-in-password'); await passwordField.click(); await passwordField.fill(params.password); - + await this.page.getByTestId('sign-in-button').click(); } async signUp(params?: { email?: string; password?: string; firstName?: string; lastName?: string }) { await this.page.goto(this.signUpUrl); + await this.openPasswordForm(); + await this.switchToSignUpForm(); const firstNameField = this.page.getByTestId('sign-up-first-name'); await firstNameField.click(); @@ -46,5 +50,89 @@ export class AuthenticationPage extends BasePage { })); await this.page.getByTestId('sign-up-button').click(); + + await this.completeOnboardingName( + `${params?.firstName || 'Bugs'} ${params?.lastName || 'Bunny'}`, + ); + } + + private async completeOnboardingName(fullName: string) { + const nameField = this.page.getByTestId('auth-full-name'); + + const nameStepShown = nameField + .waitFor({ timeout: 30000 }) + .catch(() => undefined); + const leftOnboarding = this.page + .waitForURL( + (url) => + !ONBOARDING_PATHS.some((path) => url.pathname.startsWith(path)), + { timeout: 30000 }, + ) + .catch(() => undefined); + await Promise.race([nameStepShown, leftOnboarding]); + + if (!(await nameField.count())) { + return; + } + + await nameField.fill(fullName); + await this.page.getByTestId('auth-name-continue').click(); } -} \ No newline at end of file + + private passwordFormField() { + return this.page + .getByTestId('sign-in-email') + .or(this.page.getByTestId('sign-up-email')); + } + + private async openPasswordForm() { + const usePasswordLink = this.page + .getByTestId('auth-use-password') + .or(this.page.getByRole('button', { name: 'Use password' })); + await this.passwordFormField() + .or(usePasswordLink) + .first() + .waitFor({ timeout: 30000 }); + + if (await usePasswordLink.count()) { + await usePasswordLink.first().click(); + await this.passwordFormField().first().waitFor(); + } + } + + private async switchToSignInForm() { + const signInEmail = this.page.getByTestId('sign-in-email'); + if (await signInEmail.count()) { + return; + } + + const switchMode = this.page.getByTestId('auth-switch-mode'); + if (!(await switchMode.count())) { + throw new Error( + 'The auth card offers no password sign-in: this instance has no account yet, so it only allows creating the first one.', + ); + } + + await switchMode.click(); + await signInEmail.waitFor(); + } + + private async switchToSignUpForm() { + const signUpEmail = this.page.getByTestId('sign-up-email'); + if (await signUpEmail.count()) { + return; + } + + const switchMode = this.page.getByTestId('auth-switch-mode'); + if (!(await switchMode.count())) { + throw new Error( + 'Password sign-up is unreachable on this instance: the auth card offers only the email-code flow. Set E2E_EMAIL and E2E_PASSWORD to sign in with an existing account instead.', + ); + } + + await switchMode.click(); + await signUpEmail.waitFor(); + } +} + +const ONBOARDING_PATHS = ['/sign-in', '/sign-up', '/create-platform']; diff --git a/packages/tests-e2e/pages/automations.page.ts b/packages/tests-e2e/pages/automations.page.ts index 58c46c1ccd92..64cbf1d374ac 100644 --- a/packages/tests-e2e/pages/automations.page.ts +++ b/packages/tests-e2e/pages/automations.page.ts @@ -14,7 +14,8 @@ export class AutomationsPage extends BasePage { await this.page.waitForURL( (url) => !url.pathname.includes('/sign-in') && - !url.pathname.includes('/sign-up'), + !url.pathname.includes('/sign-up') && + !url.pathname.includes('/create-platform'), { timeout: 15000 }, ); await this.visit(); diff --git a/packages/tests-e2e/scenarios/betterstack/webhook-should-return-response.flat.spec.js b/packages/tests-e2e/scenarios/betterstack/webhook-should-return-response.flat.spec.js index 5b5b81724034..caea3b552018 100644 --- a/packages/tests-e2e/scenarios/betterstack/webhook-should-return-response.flat.spec.js +++ b/packages/tests-e2e/scenarios/betterstack/webhook-should-return-response.flat.spec.js @@ -9,7 +9,16 @@ test('should handle webhook with return response', async ({ page }) => { // Sign in await page.goto(`${baseUrl}/sign-in`); - await page.getByTestId('sign-in-email').fill(email); + const passwordEmail = page.getByTestId('sign-in-email'); + const usePassword = page + .getByTestId('auth-use-password') + .or(page.getByRole('button', { name: 'Use password' })); + await passwordEmail.or(usePassword).first().waitFor({ timeout: 30000 }); + if (await usePassword.count()) { + await usePassword.first().click(); + await passwordEmail.waitFor(); + } + await passwordEmail.fill(email); await page.getByTestId('sign-in-password').fill(password); await page.getByTestId('sign-in-button').click(); diff --git a/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx b/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx index 6bba0d4a0449..2dd0c02e80b9 100644 --- a/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx +++ b/packages/web/src/features/authentication/components/auth-landing/auth-drawer-body.tsx @@ -406,6 +406,7 @@ function AuthStep({