mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
test: Migrate Cypress log-streaming tests to Playwright (#19554)
This commit is contained in:
@@ -1,105 +0,0 @@
|
|||||||
import { SettingsLogStreamingPage } from '../pages';
|
|
||||||
import { getVisibleDropdown } from '../utils';
|
|
||||||
import { getVisibleModalOverlay } from '../utils/modal';
|
|
||||||
|
|
||||||
const settingsLogStreamingPage = new SettingsLogStreamingPage();
|
|
||||||
|
|
||||||
describe('Log Streaming Settings', () => {
|
|
||||||
it('should show the unlicensed view when the feature is disabled', () => {
|
|
||||||
cy.visit('/settings/log-streaming');
|
|
||||||
settingsLogStreamingPage.getters.getActionBoxUnlicensed().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getContactUsButton().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getActionBoxLicensed().should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show the licensed view when the feature is enabled', () => {
|
|
||||||
cy.enableFeature('logStreaming');
|
|
||||||
cy.visit('/settings/log-streaming');
|
|
||||||
settingsLogStreamingPage.getters.getActionBoxLicensed().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getAddFirstDestinationButton().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getActionBoxUnlicensed().should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show the add destination modal', () => {
|
|
||||||
cy.enableFeature('logStreaming');
|
|
||||||
cy.visit('/settings/log-streaming');
|
|
||||||
settingsLogStreamingPage.actions.clickAddFirstDestination();
|
|
||||||
cy.wait(100);
|
|
||||||
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationType().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationButton().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationButton().should('have.attr', 'disabled');
|
|
||||||
settingsLogStreamingPage.getters
|
|
||||||
.getDestinationModal()
|
|
||||||
.invoke('css', 'width')
|
|
||||||
.then((widthStr) => parseInt((widthStr as unknown as string).replace('px', '')))
|
|
||||||
.should('be.lessThan', 500);
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationType().click();
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
|
|
||||||
settingsLogStreamingPage.getters
|
|
||||||
.getSelectDestinationButton()
|
|
||||||
.should('not.have.attr', 'disabled');
|
|
||||||
getVisibleModalOverlay().click(1, 1);
|
|
||||||
settingsLogStreamingPage.getters.getDestinationModal().should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a destination and delete it', () => {
|
|
||||||
cy.enableFeature('logStreaming');
|
|
||||||
cy.visit('/settings/log-streaming');
|
|
||||||
cy.wait(1000); // Race condition with getDestinationDataFromBackend()
|
|
||||||
settingsLogStreamingPage.actions.clickAddFirstDestination();
|
|
||||||
cy.wait(100);
|
|
||||||
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationType().click();
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationButton().click();
|
|
||||||
settingsLogStreamingPage.getters.getDestinationNameInput().click();
|
|
||||||
|
|
||||||
settingsLogStreamingPage.getters
|
|
||||||
.getDestinationNameInput()
|
|
||||||
.find('span[data-test-id=inline-edit-preview]')
|
|
||||||
.click({ force: true });
|
|
||||||
cy.getByTestId('inline-edit-input').type('Destination 0');
|
|
||||||
settingsLogStreamingPage.getters.getDestinationSaveButton().click();
|
|
||||||
cy.wait(100);
|
|
||||||
getVisibleModalOverlay().click(1, 1);
|
|
||||||
cy.reload();
|
|
||||||
settingsLogStreamingPage.getters.getDestinationCards().eq(0).click();
|
|
||||||
settingsLogStreamingPage.getters.getDestinationDeleteButton().should('be.visible').click();
|
|
||||||
cy.get('.el-message-box').should('be.visible').find('.btn--cancel').click();
|
|
||||||
settingsLogStreamingPage.getters.getDestinationDeleteButton().click();
|
|
||||||
cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create a destination and delete it via card actions', () => {
|
|
||||||
cy.enableFeature('logStreaming');
|
|
||||||
cy.visit('/settings/log-streaming');
|
|
||||||
cy.wait(1000); // Race condition with getDestinationDataFromBackend()
|
|
||||||
settingsLogStreamingPage.actions.clickAddFirstDestination();
|
|
||||||
cy.wait(100);
|
|
||||||
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationType().click();
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
|
|
||||||
settingsLogStreamingPage.getters.getSelectDestinationButton().click();
|
|
||||||
settingsLogStreamingPage.getters.getDestinationNameInput().click();
|
|
||||||
settingsLogStreamingPage.getters
|
|
||||||
.getDestinationNameInput()
|
|
||||||
.find('span[data-test-id=inline-edit-preview]')
|
|
||||||
.click({ force: true });
|
|
||||||
cy.getByTestId('inline-edit-input').type('Destination 1');
|
|
||||||
settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.have.attr', 'disabled');
|
|
||||||
settingsLogStreamingPage.getters.getDestinationSaveButton().click();
|
|
||||||
cy.wait(100);
|
|
||||||
getVisibleModalOverlay().click(1, 1);
|
|
||||||
cy.reload();
|
|
||||||
|
|
||||||
settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click();
|
|
||||||
getVisibleDropdown().find('.el-dropdown-menu__item').eq(0).click();
|
|
||||||
settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.exist');
|
|
||||||
getVisibleModalOverlay().click(1, 1);
|
|
||||||
|
|
||||||
settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click();
|
|
||||||
getVisibleDropdown().find('.el-dropdown-menu__item').eq(1).click();
|
|
||||||
cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
172
packages/testing/playwright/pages/SettingsLogStreamingPage.ts
Normal file
172
packages/testing/playwright/pages/SettingsLogStreamingPage.ts
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
import type { Locator } from '@playwright/test';
|
||||||
|
|
||||||
|
import { BasePage } from './BasePage';
|
||||||
|
|
||||||
|
export class SettingsLogStreamingPage extends BasePage {
|
||||||
|
getActionBoxUnlicensed(): Locator {
|
||||||
|
return this.page.getByTestId('action-box-unlicensed');
|
||||||
|
}
|
||||||
|
|
||||||
|
getActionBoxLicensed(): Locator {
|
||||||
|
return this.page.getByTestId('action-box-licensed');
|
||||||
|
}
|
||||||
|
|
||||||
|
getContactUsButton(): Locator {
|
||||||
|
return this.getActionBoxUnlicensed().locator('button');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAddFirstDestinationButton(): Locator {
|
||||||
|
return this.getActionBoxLicensed().locator('button');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDestinationModal(): Locator {
|
||||||
|
return this.page.getByTestId('destination-modal');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectDestinationType(): Locator {
|
||||||
|
return this.page.getByTestId('select-destination-type');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectDestinationTypeItems(): Locator {
|
||||||
|
return this.page.locator('.el-select-dropdown__item');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectDestinationButton(): Locator {
|
||||||
|
return this.page.getByTestId('select-destination-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDestinationNameInput(): Locator {
|
||||||
|
return this.page.getByTestId('subtitle-showing-type');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDestinationSaveButton(): Locator {
|
||||||
|
return this.page.getByTestId('destination-save-button').locator('button');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDestinationDeleteButton(): Locator {
|
||||||
|
return this.page.getByTestId('destination-delete-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDestinationCards(): Locator {
|
||||||
|
return this.page.getByTestId('destination-card');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInlineEditPreview(): Locator {
|
||||||
|
return this.page.getByTestId('inline-edit-preview');
|
||||||
|
}
|
||||||
|
|
||||||
|
getInlineEditInput(): Locator {
|
||||||
|
return this.page.getByTestId('inline-edit-input');
|
||||||
|
}
|
||||||
|
|
||||||
|
getModalOverlay(): Locator {
|
||||||
|
return this.page.locator('.el-overlay');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDropdownMenu(): Locator {
|
||||||
|
return this.page.locator('.el-dropdown-menu');
|
||||||
|
}
|
||||||
|
|
||||||
|
getDropdownMenuItem(index: number): Locator {
|
||||||
|
return this.page.locator('.el-dropdown-menu__item').nth(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfirmationDialog(): Locator {
|
||||||
|
return this.page.locator('.el-message-box');
|
||||||
|
}
|
||||||
|
|
||||||
|
getCancelButton(): Locator {
|
||||||
|
return this.page.locator('.btn--cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfirmButton(): Locator {
|
||||||
|
return this.page.locator('.btn--confirm');
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickAddFirstDestination(): Promise<void> {
|
||||||
|
await this.getAddFirstDestinationButton().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickSelectDestinationType(): Promise<void> {
|
||||||
|
await this.clickByTestId('select-destination-type');
|
||||||
|
}
|
||||||
|
|
||||||
|
async selectDestinationType(index: number): Promise<void> {
|
||||||
|
await this.getSelectDestinationTypeItems().nth(index).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickSelectDestinationButton(): Promise<void> {
|
||||||
|
await this.clickByTestId('select-destination-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickDestinationNameInput(): Promise<void> {
|
||||||
|
await this.clickByTestId('subtitle-showing-type');
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickInlineEditPreview(): Promise<void> {
|
||||||
|
// First click on the destination name input to activate it
|
||||||
|
await this.getDestinationNameInput().click();
|
||||||
|
const inlineEditPreview = this.getDestinationNameInput().locator(
|
||||||
|
'span[data-test-id="inline-edit-preview"]',
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line playwright/no-force-option
|
||||||
|
await inlineEditPreview.click({ force: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async typeDestinationName(name: string): Promise<void> {
|
||||||
|
await this.fillByTestId('inline-edit-input', name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveDestination(): Promise<void> {
|
||||||
|
await this.getDestinationSaveButton().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteDestination(): Promise<void> {
|
||||||
|
await this.clickByTestId('destination-delete-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickDestinationCard(index: number): Promise<void> {
|
||||||
|
await this.getDestinationCards().nth(index).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickDestinationCardDropdown(index: number): Promise<void> {
|
||||||
|
await this.getDestinationCards().nth(index).locator('.el-dropdown').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async clickDropdownMenuItem(index: number): Promise<void> {
|
||||||
|
await this.getDropdownMenuItem(index).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async closeModalByClickingOverlay(): Promise<void> {
|
||||||
|
await this.page
|
||||||
|
.locator('.el-overlay')
|
||||||
|
.filter({ has: this.getDestinationModal() })
|
||||||
|
.click({ position: { x: 1, y: 1 } });
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmDialog(): Promise<void> {
|
||||||
|
await this.getConfirmButton().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelDialog(): Promise<void> {
|
||||||
|
await this.getCancelButton().click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new log streaming destination with the specified name.
|
||||||
|
* Handles the full flow: modal opening, type selection, naming, and saving.
|
||||||
|
* @param destinationName - The name to give the new destination
|
||||||
|
*/
|
||||||
|
async createDestination(destinationName: string): Promise<void> {
|
||||||
|
await this.clickAddFirstDestination();
|
||||||
|
await this.getDestinationModal().waitFor({ state: 'visible' });
|
||||||
|
await this.clickSelectDestinationType();
|
||||||
|
await this.selectDestinationType(0);
|
||||||
|
await this.clickSelectDestinationButton();
|
||||||
|
await this.clickDestinationNameInput();
|
||||||
|
await this.clickInlineEditPreview();
|
||||||
|
await this.typeDestinationName(destinationName);
|
||||||
|
await this.saveDestination();
|
||||||
|
await this.closeModalByClickingOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import { NodeDetailsViewPage } from './NodeDetailsViewPage';
|
|||||||
import { NotificationsPage } from './NotificationsPage';
|
import { NotificationsPage } from './NotificationsPage';
|
||||||
import { NpsSurveyPage } from './NpsSurveyPage';
|
import { NpsSurveyPage } from './NpsSurveyPage';
|
||||||
import { ProjectSettingsPage } from './ProjectSettingsPage';
|
import { ProjectSettingsPage } from './ProjectSettingsPage';
|
||||||
|
import { SettingsLogStreamingPage } from './SettingsLogStreamingPage';
|
||||||
import { SettingsPage } from './SettingsPage';
|
import { SettingsPage } from './SettingsPage';
|
||||||
import { SidebarPage } from './SidebarPage';
|
import { SidebarPage } from './SidebarPage';
|
||||||
import { VariablesPage } from './VariablesPage';
|
import { VariablesPage } from './VariablesPage';
|
||||||
@@ -47,6 +48,7 @@ export class n8nPage {
|
|||||||
readonly npsSurvey: NpsSurveyPage;
|
readonly npsSurvey: NpsSurveyPage;
|
||||||
readonly projectSettings: ProjectSettingsPage;
|
readonly projectSettings: ProjectSettingsPage;
|
||||||
readonly settings: SettingsPage;
|
readonly settings: SettingsPage;
|
||||||
|
readonly settingsLogStreaming: SettingsLogStreamingPage;
|
||||||
readonly variables: VariablesPage;
|
readonly variables: VariablesPage;
|
||||||
readonly versions: VersionsPage;
|
readonly versions: VersionsPage;
|
||||||
readonly workerView: WorkerViewPage;
|
readonly workerView: WorkerViewPage;
|
||||||
@@ -87,6 +89,7 @@ export class n8nPage {
|
|||||||
this.npsSurvey = new NpsSurveyPage(page);
|
this.npsSurvey = new NpsSurveyPage(page);
|
||||||
this.projectSettings = new ProjectSettingsPage(page);
|
this.projectSettings = new ProjectSettingsPage(page);
|
||||||
this.settings = new SettingsPage(page);
|
this.settings = new SettingsPage(page);
|
||||||
|
this.settingsLogStreaming = new SettingsLogStreamingPage(page);
|
||||||
this.variables = new VariablesPage(page);
|
this.variables = new VariablesPage(page);
|
||||||
this.versions = new VersionsPage(page);
|
this.versions = new VersionsPage(page);
|
||||||
this.workerView = new WorkerViewPage(page);
|
this.workerView = new WorkerViewPage(page);
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { test, expect } from '../../fixtures/base';
|
||||||
|
|
||||||
|
const DESTINATION_NAMES = {
|
||||||
|
FIRST: 'Destination 0',
|
||||||
|
SECOND: 'Destination 1',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const MODAL_MAX_WIDTH = 500;
|
||||||
|
|
||||||
|
test.describe('Log Streaming Settings @db:reset', () => {
|
||||||
|
test.describe('unlicensed', () => {
|
||||||
|
test.beforeEach(async ({ api }) => {
|
||||||
|
await api.disableFeature('logStreaming');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show the unlicensed view when the feature is disabled', async ({ n8n }) => {
|
||||||
|
await n8n.navigate.toLogStreaming();
|
||||||
|
await expect(n8n.settingsLogStreaming.getActionBoxUnlicensed()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getContactUsButton()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getActionBoxLicensed()).not.toBeAttached();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('licensed', () => {
|
||||||
|
test.beforeEach(async ({ api, n8n }) => {
|
||||||
|
await api.enableFeature('logStreaming');
|
||||||
|
await n8n.navigate.toLogStreaming();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show the licensed view when the feature is enabled', async ({ n8n }) => {
|
||||||
|
await expect(n8n.settingsLogStreaming.getActionBoxLicensed()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getAddFirstDestinationButton()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getActionBoxUnlicensed()).not.toBeAttached();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show the add destination modal', async ({ n8n }) => {
|
||||||
|
await n8n.settingsLogStreaming.clickAddFirstDestination();
|
||||||
|
await expect(n8n.settingsLogStreaming.getDestinationModal()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getSelectDestinationType()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getSelectDestinationButton()).toBeVisible();
|
||||||
|
await expect(n8n.settingsLogStreaming.getSelectDestinationButton()).toBeDisabled();
|
||||||
|
|
||||||
|
const modal = n8n.settingsLogStreaming.getDestinationModal();
|
||||||
|
const width = await modal.evaluate((element) => {
|
||||||
|
return parseInt(window.getComputedStyle(element).width.replace('px', ''));
|
||||||
|
});
|
||||||
|
expect(width).toBeLessThan(MODAL_MAX_WIDTH);
|
||||||
|
|
||||||
|
await n8n.settingsLogStreaming.clickSelectDestinationType();
|
||||||
|
await n8n.settingsLogStreaming.selectDestinationType(0);
|
||||||
|
await expect(n8n.settingsLogStreaming.getSelectDestinationButton()).toBeEnabled();
|
||||||
|
await n8n.settingsLogStreaming.closeModalByClickingOverlay();
|
||||||
|
await expect(n8n.settingsLogStreaming.getDestinationModal()).not.toBeAttached();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create a destination and delete it', async ({ n8n }) => {
|
||||||
|
await n8n.settingsLogStreaming.createDestination(DESTINATION_NAMES.FIRST);
|
||||||
|
await n8n.page.reload();
|
||||||
|
await n8n.settingsLogStreaming.clickDestinationCard(0);
|
||||||
|
await expect(n8n.settingsLogStreaming.getDestinationDeleteButton()).toBeVisible();
|
||||||
|
await n8n.settingsLogStreaming.deleteDestination();
|
||||||
|
await expect(n8n.settingsLogStreaming.getConfirmationDialog()).toBeVisible();
|
||||||
|
await n8n.settingsLogStreaming.cancelDialog();
|
||||||
|
await n8n.settingsLogStreaming.deleteDestination();
|
||||||
|
await expect(n8n.settingsLogStreaming.getConfirmationDialog()).toBeVisible();
|
||||||
|
await n8n.settingsLogStreaming.confirmDialog();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create a destination and delete it via card actions', async ({ n8n }) => {
|
||||||
|
await n8n.settingsLogStreaming.createDestination(DESTINATION_NAMES.SECOND);
|
||||||
|
await n8n.page.reload();
|
||||||
|
|
||||||
|
await n8n.settingsLogStreaming.clickDestinationCardDropdown(0);
|
||||||
|
await n8n.settingsLogStreaming.clickDropdownMenuItem(0);
|
||||||
|
await expect(n8n.settingsLogStreaming.getDestinationSaveButton()).not.toBeAttached();
|
||||||
|
await n8n.settingsLogStreaming.closeModalByClickingOverlay();
|
||||||
|
|
||||||
|
await n8n.settingsLogStreaming.clickDestinationCardDropdown(0);
|
||||||
|
await n8n.settingsLogStreaming.clickDropdownMenuItem(1);
|
||||||
|
await expect(n8n.settingsLogStreaming.getConfirmationDialog()).toBeVisible();
|
||||||
|
await n8n.settingsLogStreaming.confirmDialog();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user