Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d31309a92d |
4
.github/workflows/playwright.yml
vendored
4
.github/workflows/playwright.yml
vendored
@@ -12,6 +12,9 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BULLMQ_DASHBOARD_USERNAME: root
|
||||
BULLMQ_DASHBOARD_PASSWORD: sample
|
||||
ENABLE_BULLMQ_DASHBOARD: true
|
||||
ENCRYPTION_KEY: sample_encryption_key
|
||||
WEBHOOK_SECRET_KEY: sample_webhook_secret_key
|
||||
APP_SECRET_KEY: sample_app_secret_key
|
||||
@@ -22,6 +25,7 @@ env:
|
||||
POSTGRES_PASSWORD: automatisch_password
|
||||
REDIS_HOST: localhost
|
||||
APP_ENV: production
|
||||
PORT: 3000
|
||||
LICENSE_KEY: dummy_license_key
|
||||
|
||||
jobs:
|
||||
|
@@ -2,4 +2,6 @@ POSTGRES_DB=automatisch
|
||||
POSTGRES_USER=automatisch_user
|
||||
POSTGRES_PASSWORD=automatisch_password
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_HOST=localhost
|
||||
BULLMQ_DASHBOARD_PASSWORD=sample
|
||||
BULLMQ_DASHBOARD_USERNAME=root
|
35
packages/e2e-tests/fixtures/flows-page.js
Normal file
35
packages/e2e-tests/fixtures/flows-page.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const { AuthenticatedPage } = require('./authenticated-page');
|
||||
const { expect } = require('@playwright/test');
|
||||
|
||||
export class FlowsPage extends AuthenticatedPage {
|
||||
constructor(page) {
|
||||
super(page);
|
||||
|
||||
this.flowRow = this.page.getByTestId('flow-row');
|
||||
this.flowCard = this.page.getByTestId('card-action-area');
|
||||
this.deleteFlowMenuItem = this.page.getByRole('menuitem', {
|
||||
name: 'Delete',
|
||||
});
|
||||
}
|
||||
|
||||
async clickOnDeleteFlowMenuItem() {
|
||||
await this.deleteFlowMenuItem.click();
|
||||
}
|
||||
|
||||
async deleteFlow(flowId) {
|
||||
const desiredFlow = await this.flowRow.filter({
|
||||
has: this.page.locator(`a[href="/editor/${flowId}"]`),
|
||||
});
|
||||
await desiredFlow.locator('button').click();
|
||||
await this.clickOnDeleteFlowMenuItem();
|
||||
|
||||
await expect(
|
||||
await this.flowRow.filter({
|
||||
has: this.page.locator(`a[href="/editor/${flowId}"]`),
|
||||
})
|
||||
).toHaveCount(0);
|
||||
|
||||
const snackbar = await this.getSnackbarData();
|
||||
await expect(snackbar.variant).toBe('success');
|
||||
}
|
||||
}
|
@@ -9,6 +9,7 @@ const { AcceptInvitation } = require('./accept-invitation-page');
|
||||
const { adminFixtures } = require('./admin');
|
||||
const { AdminSetupPage } = require('./admin-setup-page');
|
||||
const { AdminCreateUserPage } = require('./admin/create-user-page');
|
||||
const { FlowsPage } = require('./flows-page');
|
||||
|
||||
exports.test = test.extend({
|
||||
page: async ({ page }, use) => {
|
||||
@@ -35,6 +36,9 @@ exports.test = test.extend({
|
||||
userInterfacePage: async ({ page }, use) => {
|
||||
await use(new UserInterfacePage(page));
|
||||
},
|
||||
flowsPage: async ({ page }, use) => {
|
||||
await use(new FlowsPage(page));
|
||||
},
|
||||
...adminFixtures,
|
||||
});
|
||||
|
||||
|
20
packages/e2e-tests/helpers/bullmq-helper.js
Normal file
20
packages/e2e-tests/helpers/bullmq-helper.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { expect } = require('../fixtures/index');
|
||||
|
||||
export const expectNoDelayedJobForFlow = async (request, flowId) => {
|
||||
const token = btoa(
|
||||
`${process.env.BULLMQ_DASHBOARD_USERNAME}:${process.env.BULLMQ_DASHBOARD_PASSWORD}`
|
||||
);
|
||||
const queues = await request.get(
|
||||
`http://localhost:${process.env.PORT}/admin/queues/api/queues?activeQueue=flow&status=delayed&page=1`,
|
||||
{
|
||||
headers: { Authorization: `Basic ${token}` },
|
||||
}
|
||||
);
|
||||
const queuesJsonResponse = await queues.json();
|
||||
const flowQueue = queuesJsonResponse.queues.find(
|
||||
(queue) => queue.name === 'flow'
|
||||
);
|
||||
await expect(
|
||||
flowQueue.jobs.find((job) => job.name === `flow-${flowId}`)
|
||||
).toBeUndefined();
|
||||
};
|
54
packages/e2e-tests/helpers/flow-api-helper.js
Normal file
54
packages/e2e-tests/helpers/flow-api-helper.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const { expect } = require('../fixtures/index');
|
||||
|
||||
export const createFlow = async (request, token) => {
|
||||
const response = await request.post(
|
||||
`http://localhost:${process.env.PORT}/api/v1/flows`,
|
||||
{ headers: { Authorization: token } }
|
||||
);
|
||||
await expect(response.status()).toBe(201);
|
||||
return await response.json();
|
||||
};
|
||||
|
||||
export const updateFlowName = async (request, token, flowId) => {
|
||||
const updateFlowNameResponse = await request.patch(
|
||||
`http://localhost:${process.env.PORT}/api/v1/flows/${flowId}`,
|
||||
{
|
||||
headers: { Authorization: token },
|
||||
data: { name: flowId },
|
||||
}
|
||||
);
|
||||
await expect(updateFlowNameResponse.status()).toBe(200);
|
||||
};
|
||||
|
||||
export const updateFlowStep = async (request, token, stepId, requestBody) => {
|
||||
const updateTriggerStepResponse = await request.patch(
|
||||
`http://localhost:${process.env.PORT}/api/v1/steps/${stepId}`,
|
||||
{
|
||||
headers: { Authorization: token },
|
||||
data: requestBody,
|
||||
}
|
||||
);
|
||||
await expect(updateTriggerStepResponse.status()).toBe(200);
|
||||
return await updateTriggerStepResponse.json();
|
||||
};
|
||||
|
||||
export const testStep = async (request, token, stepId) => {
|
||||
const testTriggerStepResponse = await request.post(
|
||||
`http://localhost:${process.env.PORT}/api/v1/steps/${stepId}/test`,
|
||||
{
|
||||
headers: { Authorization: token },
|
||||
}
|
||||
);
|
||||
await expect(testTriggerStepResponse.status()).toBe(200);
|
||||
};
|
||||
|
||||
export const publishFlow = async (request, token, flowId) => {
|
||||
const publishFlowResponse = await request.patch(
|
||||
`http://localhost:${process.env.PORT}/api/v1/flows/${flowId}/status`,
|
||||
{
|
||||
headers: { Authorization: token },
|
||||
data: { active: true },
|
||||
}
|
||||
);
|
||||
await expect(publishFlowResponse.status()).toBe(200);
|
||||
};
|
213
packages/e2e-tests/tests/flow/delete-flow.spec.js
Normal file
213
packages/e2e-tests/tests/flow/delete-flow.spec.js
Normal file
@@ -0,0 +1,213 @@
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
const { expectNoDelayedJobForFlow } = require('../../helpers/bullmq-helper');
|
||||
const {
|
||||
createFlow,
|
||||
updateFlowName,
|
||||
updateFlowStep,
|
||||
testStep,
|
||||
publishFlow,
|
||||
} = require('../../helpers/flow-api-helper');
|
||||
|
||||
let tokenJsonResponse;
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
const tokenResponse = await request.post(
|
||||
`http://localhost:${process.env.PORT}/api/v1/access-tokens`,
|
||||
{
|
||||
data: {
|
||||
email: process.env.LOGIN_EMAIL,
|
||||
password: process.env.LOGIN_PASSWORD,
|
||||
},
|
||||
}
|
||||
);
|
||||
await expect(tokenResponse.status()).toBe(200);
|
||||
tokenJsonResponse = await tokenResponse.json();
|
||||
});
|
||||
|
||||
test('Empty flow can be deleted', async ({ page, request, flowsPage }) => {
|
||||
const flow = await createFlow(request, tokenJsonResponse.data.token);
|
||||
const flowId = flow.data.id;
|
||||
await updateFlowName(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
await page.reload();
|
||||
|
||||
await flowsPage.deleteFlow(flowId);
|
||||
});
|
||||
|
||||
test('Completed webhook flow can be deleted', async ({
|
||||
page,
|
||||
request,
|
||||
flowsPage,
|
||||
}) => {
|
||||
const flow = await createFlow(request, tokenJsonResponse.data.token);
|
||||
const flowId = flow.data.id;
|
||||
const flowSteps = flow.data.steps;
|
||||
await updateFlowName(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
|
||||
const actionStepId = flowSteps.find((step) => step.type === 'action').id;
|
||||
|
||||
const triggerStep = await updateFlowStep(
|
||||
request,
|
||||
tokenJsonResponse.data.token,
|
||||
triggerStepId,
|
||||
{
|
||||
appKey: 'webhook',
|
||||
key: 'catchRawWebhook',
|
||||
parameters: {
|
||||
workSynchronously: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
await testStep(request, tokenJsonResponse.data.token, triggerStepId);
|
||||
|
||||
await updateFlowStep(request, tokenJsonResponse.data.token, actionStepId, {
|
||||
appKey: 'webhook',
|
||||
key: 'respondWith',
|
||||
parameters: {
|
||||
statusCode: '200',
|
||||
body: 'ok',
|
||||
headers: [
|
||||
{
|
||||
key: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
await testStep(request, tokenJsonResponse.data.token, actionStepId);
|
||||
|
||||
await page.reload();
|
||||
|
||||
await flowsPage.deleteFlow(flowId);
|
||||
const triggerWebhookResponse = await request.get(triggerStep.data.webhookUrl);
|
||||
await expect(triggerWebhookResponse.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('Completed poll flow can be deleted', async ({
|
||||
page,
|
||||
request,
|
||||
flowsPage,
|
||||
}) => {
|
||||
const flow = await createFlow(request, tokenJsonResponse.data.token);
|
||||
const flowId = flow.data.id;
|
||||
const flowSteps = flow.data.steps;
|
||||
await updateFlowName(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
|
||||
const actionStepId = flowSteps.find((step) => step.type === 'action').id;
|
||||
|
||||
await updateFlowStep(request, tokenJsonResponse.data.token, triggerStepId, {
|
||||
appKey: 'rss',
|
||||
key: 'newItemsInFeed',
|
||||
parameters: { feedUrl: 'https://feeds.bbci.co.uk/news/rss.xml' },
|
||||
});
|
||||
await testStep(request, tokenJsonResponse.data.token, triggerStepId);
|
||||
|
||||
await updateFlowStep(request, tokenJsonResponse.data.token, actionStepId, {
|
||||
appKey: 'datastore',
|
||||
key: 'setValue',
|
||||
parameters: {
|
||||
key: 'newsTitle',
|
||||
value: '{{step.' + triggerStepId + '.title}}',
|
||||
},
|
||||
});
|
||||
await testStep(request, tokenJsonResponse.data.token, actionStepId);
|
||||
|
||||
await page.reload();
|
||||
|
||||
await flowsPage.deleteFlow(flowId);
|
||||
|
||||
await expectNoDelayedJobForFlow(request, flowId);
|
||||
});
|
||||
|
||||
test('Published webhook flow can be deleted', async ({
|
||||
page,
|
||||
request,
|
||||
flowsPage,
|
||||
}) => {
|
||||
const flow = await createFlow(request, tokenJsonResponse.data.token);
|
||||
const flowId = flow.data.id;
|
||||
const flowSteps = flow.data.steps;
|
||||
await updateFlowName(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
|
||||
const actionStepId = flowSteps.find((step) => step.type === 'action').id;
|
||||
|
||||
const triggerStep = await updateFlowStep(
|
||||
request,
|
||||
tokenJsonResponse.data.token,
|
||||
triggerStepId,
|
||||
{
|
||||
appKey: 'webhook',
|
||||
key: 'catchRawWebhook',
|
||||
parameters: {
|
||||
workSynchronously: false,
|
||||
},
|
||||
}
|
||||
);
|
||||
await testStep(request, tokenJsonResponse.data.token, triggerStepId);
|
||||
|
||||
await updateFlowStep(request, tokenJsonResponse.data.token, actionStepId, {
|
||||
appKey: 'webhook',
|
||||
key: 'respondWith',
|
||||
parameters: {
|
||||
statusCode: '200',
|
||||
body: 'ok',
|
||||
headers: [
|
||||
{
|
||||
key: '',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
await testStep(request, tokenJsonResponse.data.token, actionStepId);
|
||||
|
||||
await publishFlow(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
await page.reload();
|
||||
|
||||
await flowsPage.deleteFlow(flowId);
|
||||
const triggerWebhookResponse = await request.get(triggerStep.data.webhookUrl);
|
||||
await expect(triggerWebhookResponse.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('Published poll flow can be deleted', async ({
|
||||
page,
|
||||
request,
|
||||
flowsPage,
|
||||
}) => {
|
||||
const flow = await createFlow(request, tokenJsonResponse.data.token);
|
||||
const flowId = flow.data.id;
|
||||
const flowSteps = flow.data.steps;
|
||||
await updateFlowName(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
const triggerStepId = flowSteps.find((step) => step.type === 'trigger').id;
|
||||
const actionStepId = flowSteps.find((step) => step.type === 'action').id;
|
||||
|
||||
await updateFlowStep(request, tokenJsonResponse.data.token, triggerStepId, {
|
||||
appKey: 'rss',
|
||||
key: 'newItemsInFeed',
|
||||
parameters: { feedUrl: 'https://feeds.bbci.co.uk/news/rss.xml' },
|
||||
});
|
||||
await testStep(request, tokenJsonResponse.data.token, triggerStepId);
|
||||
|
||||
await updateFlowStep(request, tokenJsonResponse.data.token, actionStepId, {
|
||||
appKey: 'datastore',
|
||||
key: 'setValue',
|
||||
parameters: {
|
||||
key: 'newsTitle',
|
||||
value: '{{step.' + triggerStepId + '.title}}',
|
||||
},
|
||||
});
|
||||
await testStep(request, tokenJsonResponse.data.token, actionStepId);
|
||||
|
||||
await publishFlow(request, tokenJsonResponse.data.token, flowId);
|
||||
|
||||
await page.reload();
|
||||
|
||||
await flowsPage.deleteFlow(flowId);
|
||||
|
||||
await expectNoDelayedJobForFlow(request, flowId);
|
||||
});
|
Reference in New Issue
Block a user