test: add delete flow tests

This commit is contained in:
Jakub P.
2024-10-16 23:33:43 +02:00
parent 0234b4ad81
commit d31309a92d
8 changed files with 333 additions and 1 deletions

View File

@@ -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:

View File

@@ -3,3 +3,5 @@ POSTGRES_USER=automatisch_user
POSTGRES_PASSWORD=automatisch_password
POSTGRES_PORT=5432
POSTGRES_HOST=localhost
BULLMQ_DASHBOARD_PASSWORD=sample
BULLMQ_DASHBOARD_USERNAME=root

View 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');
}
}

View File

@@ -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,
});

View 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();
};

View 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);
};

View 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);
});