diff --git a/packages/e2e-tests/fixtures/flow-editor-page.js b/packages/e2e-tests/fixtures/flow-editor-page.js index b7de2d98..371b90bc 100644 --- a/packages/e2e-tests/fixtures/flow-editor-page.js +++ b/packages/e2e-tests/fixtures/flow-editor-page.js @@ -1,4 +1,6 @@ const { AuthenticatedPage } = require('./authenticated-page'); +const { expect } = require('@playwright/test'); +const axios = require('axios'); export class FlowEditorPage extends AuthenticatedPage { screenshotPath = '/flow-editor'; @@ -9,17 +11,74 @@ export class FlowEditorPage extends AuthenticatedPage { constructor(page) { super(page); + this.page = page; this.appAutocomplete = this.page.getByTestId('choose-app-autocomplete'); this.eventAutocomplete = this.page.getByTestId('choose-event-autocomplete'); this.continueButton = this.page.getByTestId('flow-substep-continue-button'); + this.testAndContinueButton = this.page.getByText('Test & Continue'); this.connectionAutocomplete = this.page.getByTestId( 'choose-connection-autocomplete' ); - this.testOuput = this.page.getByTestId('flow-test-substep-output'); + this.testOutput = this.page.getByTestId('flow-test-substep-output'); + this.hasNoOutput = this.page.getByTestId('flow-test-substep-no-output'); this.unpublishFlowButton = this.page.getByTestId('unpublish-flow-button'); this.publishFlowButton = this.page.getByTestId('publish-flow-button'); this.infoSnackbar = this.page.getByTestId('flow-cannot-edit-info-snackbar'); this.trigger = this.page.getByLabel('Trigger on weekends?'); this.stepCircularLoader = this.page.getByTestId('step-circular-loader'); + this.flowName = this.page.getByTestId('editableTypography'); + this.flowNameInput = this.page + .getByTestId('editableTypographyInput') + .locator('input'); + } + + async createWebhookTrigger(workSynchronously) { + await this.appAutocomplete.click(); + await this.page.getByRole('option', { name: 'Webhook' }).click(); + + await expect(this.eventAutocomplete).toBeVisible(); + await this.eventAutocomplete.click(); + await this.page.getByRole('option', { name: 'Catch raw webhook' }).click(); + await this.continueButton.click(); + await this.page + .getByTestId('parameters.workSynchronously-autocomplete') + .click(); + await this.page + .getByRole('option', { name: workSynchronously ? 'Yes' : 'No' }) + .click(); + await this.continueButton.click(); + + const webhookUrl = this.page.locator('input[name="webhookUrl"]'); + if (workSynchronously) { + await expect(webhookUrl).toHaveValue(/sync/); + } else { + await expect(webhookUrl).not.toHaveValue(/sync/); + } + + const triggerResponse = await axios.get(await webhookUrl.inputValue()); + await expect(triggerResponse.status).toBe(204); + + await expect(this.testOutput).not.toBeVisible(); + await this.testAndContinueButton.click(); + await expect(this.testOutput).toBeVisible(); + await expect(this.hasNoOutput).not.toBeVisible(); + await this.continueButton.click(); + + return await webhookUrl.inputValue(); + } + + async chooseAppAndEvent(appName, eventName) { + await this.appAutocomplete.click(); + await this.page.getByRole('option', { name: appName }).click(); + await expect(this.eventAutocomplete).toBeVisible(); + await this.eventAutocomplete.click(); + await this.page.getByRole('option', { name: eventName }).click(); + await this.continueButton.click(); + } + + async testAndContinue() { + await this.continueButton.last().click(); + await expect(this.testOutput).toBeVisible(); + await this.continueButton.click(); } } diff --git a/packages/e2e-tests/tests/app-integrations/webhook.spec.js b/packages/e2e-tests/tests/app-integrations/webhook.spec.js new file mode 100644 index 00000000..8d4db79a --- /dev/null +++ b/packages/e2e-tests/tests/app-integrations/webhook.spec.js @@ -0,0 +1,82 @@ +const { test, expect } = require('../../fixtures/index'); +const axios = require('axios'); + +test.describe('Webhook flow', () => { + test.beforeEach(async ({ page }) => { + await page.getByTestId('create-flow-button').click(); + await page.waitForURL( + /\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/ + ); + await expect(page.getByTestId('flow-step')).toHaveCount(2); + }); + + test('Create a new flow with a sync Webhook step then a Webhook step', async ({ + flowEditorPage, + page, + }) => { + await flowEditorPage.flowName.click(); + await flowEditorPage.flowNameInput.fill('syncWebhook'); + const syncWebhookUrl = await flowEditorPage.createWebhookTrigger(true); + + await flowEditorPage.chooseAppAndEvent('Webhook', 'Respond with'); + + await expect(flowEditorPage.continueButton.last()).not.toBeEnabled(); + + await page + .getByTestId('parameters.statusCode-power-input') + .locator('[contenteditable]') + .fill('200'); + await flowEditorPage.clickAway(); + await expect(flowEditorPage.continueButton.last()).not.toBeEnabled(); + + await page + .getByTestId('parameters.body-power-input') + .locator('[contenteditable]') + .fill('response from webhook'); + await flowEditorPage.clickAway(); + await expect(flowEditorPage.continueButton).toBeEnabled(); + await flowEditorPage.continueButton.click(); + + await flowEditorPage.testAndContinue(); + await flowEditorPage.publishFlowButton.click(); + + const response = await axios.get(syncWebhookUrl); + await expect(response.status).toBe(200); + await expect(response.data).toBe('response from webhook'); + }); + + test('Create a new flow with an async Webhook step then a Webhook step', async ({ + flowEditorPage, + page, + }) => { + await flowEditorPage.flowName.click(); + await flowEditorPage.flowNameInput.fill('asyncWebhook'); + const asyncWebhookUrl = await flowEditorPage.createWebhookTrigger(false); + + await flowEditorPage.chooseAppAndEvent('Webhook', 'Respond with'); + + await expect(flowEditorPage.continueButton.last()).not.toBeEnabled(); + + await page + .getByTestId('parameters.statusCode-power-input') + .locator('[contenteditable]') + .fill('200'); + await flowEditorPage.clickAway(); + await expect(flowEditorPage.continueButton.last()).not.toBeEnabled(); + + await page + .getByTestId('parameters.body-power-input') + .locator('[contenteditable]') + .fill('response from webhook'); + await flowEditorPage.clickAway(); + await expect(flowEditorPage.continueButton).toBeEnabled(); + await flowEditorPage.continueButton.click(); + + await flowEditorPage.testAndContinue(); + await flowEditorPage.publishFlowButton.click(); + + const response = await axios.get(asyncWebhookUrl); + await expect(response.status).toBe(204); + await expect(response.data).toBe(''); + }); +}); diff --git a/packages/e2e-tests/tests/flow-editor/create-flow.spec.js b/packages/e2e-tests/tests/flow-editor/create-flow.spec.js index 43d82861..98114c27 100644 --- a/packages/e2e-tests/tests/flow-editor/create-flow.spec.js +++ b/packages/e2e-tests/tests/flow-editor/create-flow.spec.js @@ -2,7 +2,6 @@ const { test, expect } = require('../../fixtures/index'); test('Ensure creating a new flow works', async ({ page }) => { await page.getByTestId('create-flow-button').click(); - await expect(page).toHaveURL(/\/editor\/create/); await expect(page).toHaveURL( /\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/ ); @@ -69,9 +68,9 @@ test( await test.step('test trigger', async () => { await test.step('show sample output', async () => { - await expect(flowEditorPage.testOuput).not.toBeVisible(); + await expect(flowEditorPage.testOutput).not.toBeVisible(); await flowEditorPage.continueButton.click(); - await expect(flowEditorPage.testOuput).toBeVisible(); + await expect(flowEditorPage.testOutput).toBeVisible(); await flowEditorPage.screenshot({ path: 'Scheduler trigger test output.png', }); @@ -143,12 +142,12 @@ test( await test.step('test trigger substep', async () => { await test.step('show sample output', async () => { - await expect(flowEditorPage.testOuput).not.toBeVisible(); + await expect(flowEditorPage.testOutput).not.toBeVisible(); await page .getByTestId('flow-substep-continue-button') .first() .click(); - await expect(flowEditorPage.testOuput).toBeVisible(); + await expect(flowEditorPage.testOutput).toBeVisible(); await flowEditorPage.screenshot({ path: 'Ntfy action test output.png', }); diff --git a/packages/web/src/components/EditableTypography/index.jsx b/packages/web/src/components/EditableTypography/index.jsx index b30fcb1e..bb234326 100644 --- a/packages/web/src/components/EditableTypography/index.jsx +++ b/packages/web/src/components/EditableTypography/index.jsx @@ -41,6 +41,7 @@ function EditableTypography(props) { if (editing) { component = ( + {formatMessage('flowEditor.noTestDataTitle')}