mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
test: move import test from cypress to RTL to make it less flaky (#6982)
This commit is contained in:
parent
d698eccb4a
commit
d01100fbf0
1
.github/workflows/e2e.frontend.yaml
vendored
1
.github/workflows/e2e.frontend.yaml
vendored
@ -12,7 +12,6 @@ jobs:
|
||||
- projects/access.spec.ts
|
||||
- projects/overview.spec.ts
|
||||
- segments/segments.spec.ts
|
||||
- import/import.spec.ts
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
|
@ -1,136 +0,0 @@
|
||||
///<reference path="../../global.d.ts" />
|
||||
|
||||
describe('imports', () => {
|
||||
const baseUrl = Cypress.config().baseUrl;
|
||||
const randomSeed = String(Math.random()).split('.')[1];
|
||||
const randomFeatureName = `cypress-features${randomSeed}`;
|
||||
const userIds: any[] = [];
|
||||
|
||||
before(() => {
|
||||
cy.runBefore();
|
||||
cy.login_UI();
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
cy.request('POST', `${baseUrl}/api/admin/user-admin`, {
|
||||
name: `unleash-e2e-user${i}-${randomFeatureName}`,
|
||||
email: `unleash-e2e-user${i}-${randomFeatureName}@test.com`,
|
||||
sendEmail: false,
|
||||
rootRole: 3,
|
||||
}).then((response) => userIds.push(response.body.id));
|
||||
}
|
||||
});
|
||||
|
||||
after(() => {
|
||||
userIds.forEach((id) =>
|
||||
cy.request('DELETE', `${baseUrl}/api/admin/user-admin/${id}`),
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login_UI();
|
||||
if (document.querySelector("[data-testid='CLOSE_SPLASH']")) {
|
||||
cy.get("[data-testid='CLOSE_SPLASH']").click();
|
||||
}
|
||||
});
|
||||
|
||||
it('can import data', () => {
|
||||
cy.visit('/projects/default');
|
||||
cy.get("[data-testid='IMPORT_BUTTON']").click({ force: true });
|
||||
|
||||
const exportText = {
|
||||
features: [
|
||||
{
|
||||
name: randomFeatureName,
|
||||
description: '',
|
||||
type: 'release',
|
||||
project: 'default',
|
||||
stale: false,
|
||||
impressionData: false,
|
||||
archived: false,
|
||||
},
|
||||
],
|
||||
featureStrategies: [
|
||||
{
|
||||
name: 'flexibleRollout',
|
||||
id: '14a0d9dd-2b5d-4a21-98fd-ede72bda0328',
|
||||
featureName: randomFeatureName,
|
||||
parameters: {
|
||||
groupId: randomFeatureName,
|
||||
rollout: '50',
|
||||
stickiness: 'default',
|
||||
},
|
||||
constraints: [],
|
||||
segments: [],
|
||||
},
|
||||
],
|
||||
featureEnvironments: [
|
||||
{
|
||||
enabled: true,
|
||||
featureName: randomFeatureName,
|
||||
environment: 'test',
|
||||
variants: [],
|
||||
name: randomFeatureName,
|
||||
},
|
||||
],
|
||||
contextFields: [],
|
||||
featureTags: [
|
||||
{
|
||||
featureName: randomFeatureName,
|
||||
tagType: 'simple',
|
||||
tagValue: 'best-tag',
|
||||
},
|
||||
{
|
||||
featureName: randomFeatureName,
|
||||
tagType: 'simple',
|
||||
tagValue: 'rserw',
|
||||
},
|
||||
{
|
||||
featureName: randomFeatureName,
|
||||
tagType: 'simple',
|
||||
tagValue: 'FARO',
|
||||
},
|
||||
],
|
||||
segments: [],
|
||||
tagTypes: [
|
||||
{
|
||||
name: 'simple',
|
||||
description: 'Used to simplify filtering of features',
|
||||
icon: '#',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
cy.get("[data-testid='VALIDATE_BUTTON']").should('be.disabled');
|
||||
|
||||
cy.intercept('POST', '/api/admin/features-batch/import').as(
|
||||
'featureImported',
|
||||
);
|
||||
|
||||
// cypress can only work with input@file that is visible
|
||||
cy.get('input[type=file]')
|
||||
.invoke('attr', 'style', 'display: block')
|
||||
.selectFile({
|
||||
contents: Cypress.Buffer.from(JSON.stringify(exportText)),
|
||||
fileName: 'upload.json',
|
||||
lastModified: Date.now(),
|
||||
});
|
||||
cy.get("[data-testid='VALIDATE_BUTTON']").click();
|
||||
cy.get("[data-testid='IMPORT_CONFIGURATION_BUTTON']").click();
|
||||
|
||||
cy.wait('@featureImported');
|
||||
cy.contains('Import completed');
|
||||
|
||||
cy.request({
|
||||
url: `/api/admin/projects/default/features/${randomFeatureName}`,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}).then((response) => {
|
||||
expect(response.body.name).to.equal(randomFeatureName);
|
||||
const devEnv = response.body.environments.find(
|
||||
(env: any) => env.name === 'development',
|
||||
);
|
||||
|
||||
expect(devEnv.name).to.equal('development');
|
||||
expect(devEnv.strategies[0].parameters.rollout).to.equal('50');
|
||||
expect(devEnv.enabled).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
151
frontend/src/component/project/Project/Import/Import.test.tsx
Normal file
151
frontend/src/component/project/Project/Import/Import.test.tsx
Normal file
@ -0,0 +1,151 @@
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { ImportModal } from './ImportModal';
|
||||
import { testServerRoute, testServerSetup } from 'utils/testServer';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { CREATE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
|
||||
const server = testServerSetup();
|
||||
|
||||
const setupApi = () => {
|
||||
testServerRoute(server, '/api/admin/ui-config', {
|
||||
versionInfo: {
|
||||
current: { enterprise: 'present' },
|
||||
},
|
||||
});
|
||||
testServerRoute(server, '/api/admin/projects/default', {
|
||||
environments: [
|
||||
{ environment: 'development' },
|
||||
{ environment: 'production' },
|
||||
],
|
||||
});
|
||||
testServerRoute(
|
||||
server,
|
||||
'/api/admin/features-batch/validate',
|
||||
{ errors: [], permissions: [], warnings: [] },
|
||||
'post',
|
||||
);
|
||||
testServerRoute(server, '/api/admin/features-batch/import', {}, 'post');
|
||||
};
|
||||
|
||||
const importFile = async (content: string) => {
|
||||
const selectFileInput = screen.getByTestId('import-file');
|
||||
const importFile = new File([content], 'import.json', {
|
||||
type: 'application/json',
|
||||
});
|
||||
userEvent.upload(selectFileInput, importFile);
|
||||
};
|
||||
|
||||
test('Import happy path', async () => {
|
||||
setupApi();
|
||||
let closed = false;
|
||||
const setOpen = (open: boolean) => {
|
||||
closed = !open;
|
||||
};
|
||||
render(<ImportModal open={true} setOpen={setOpen} project='default' />, {
|
||||
permissions: [{ permission: CREATE_FEATURE }],
|
||||
});
|
||||
|
||||
// configure stage
|
||||
screen.getByText('Import options');
|
||||
screen.getByText('Drop your file here');
|
||||
const validateButton = screen.getByText('Validate');
|
||||
expect(validateButton).toBeDisabled();
|
||||
|
||||
await importFile('{}');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Validate')).toBeEnabled();
|
||||
});
|
||||
|
||||
const codeEditorLabel = screen.getByText('Code editor');
|
||||
codeEditorLabel.click();
|
||||
const editor = screen.getByLabelText('Exported toggles');
|
||||
expect(editor.textContent).toBe('{}');
|
||||
|
||||
screen.getByText('Validate').click();
|
||||
|
||||
// validate stage
|
||||
screen.getByText('You are importing this configuration in:');
|
||||
screen.getByText('development');
|
||||
screen.getByText('default');
|
||||
const importButton = screen.getByText('Import configuration');
|
||||
expect(importButton).toBeEnabled();
|
||||
importButton.click();
|
||||
|
||||
// import stage
|
||||
await screen.findByText('Importing...');
|
||||
await screen.findByText('Import completed');
|
||||
|
||||
expect(closed).toBe(false);
|
||||
const closeButton = screen.getByText('Close');
|
||||
closeButton.click();
|
||||
expect(closed).toBe(true);
|
||||
});
|
||||
|
||||
test('Block when importing non json content', async () => {
|
||||
setupApi();
|
||||
const setOpen = () => {};
|
||||
render(<ImportModal open={true} setOpen={setOpen} project='default' />, {
|
||||
permissions: [{ permission: CREATE_FEATURE }],
|
||||
});
|
||||
|
||||
const codeEditorLabel = screen.getByText('Code editor');
|
||||
codeEditorLabel.click();
|
||||
const editor = screen.getByLabelText('Exported toggles');
|
||||
userEvent.type(editor, 'invalid non json');
|
||||
|
||||
const validateButton = screen.getByText('Validate');
|
||||
expect(validateButton).toBeDisabled();
|
||||
});
|
||||
|
||||
test('Show validation errors', async () => {
|
||||
setupApi();
|
||||
testServerRoute(
|
||||
server,
|
||||
'/api/admin/features-batch/validate',
|
||||
{
|
||||
errors: [
|
||||
{ message: 'error message', affectedItems: ['itemC', 'itemD'] },
|
||||
],
|
||||
permissions: [
|
||||
{
|
||||
message: 'permission message',
|
||||
affectedItems: ['itemE', 'itemF'],
|
||||
},
|
||||
],
|
||||
warnings: [
|
||||
{
|
||||
message: 'warning message',
|
||||
affectedItems: ['itemA', 'itemB'],
|
||||
},
|
||||
],
|
||||
},
|
||||
'post',
|
||||
);
|
||||
const setOpen = () => {};
|
||||
render(<ImportModal open={true} setOpen={setOpen} project='default' />, {
|
||||
permissions: [{ permission: CREATE_FEATURE }],
|
||||
});
|
||||
|
||||
await importFile('{}');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Validate')).toBeEnabled();
|
||||
});
|
||||
|
||||
screen.getByText('Validate').click();
|
||||
|
||||
await screen.findByText('warning message');
|
||||
await screen.findByText('itemA');
|
||||
await screen.findByText('itemB');
|
||||
|
||||
await screen.findByText('error message');
|
||||
await screen.findByText('itemC');
|
||||
await screen.findByText('itemD');
|
||||
|
||||
await screen.findByText('permission message');
|
||||
await screen.findByText('itemE');
|
||||
await screen.findByText('itemF');
|
||||
|
||||
const importButton = screen.getByText('Import configuration');
|
||||
expect(importButton).toBeDisabled();
|
||||
});
|
@ -52,7 +52,7 @@ export const FileDropZone: FC<IFileDropZoneProps> = ({
|
||||
|
||||
return (
|
||||
<Box {...getRootProps()} {...props}>
|
||||
<input {...getInputProps()} />
|
||||
<input data-testid='import-file' {...getInputProps()} />
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
|
@ -85,7 +85,12 @@
|
||||
"^.+\\.tsx?$": ["@swc/jest"]
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||
"testPathIgnorePatterns": ["/dist/", "/node_modules/", "/frontend/"],
|
||||
"testPathIgnorePatterns": [
|
||||
"/dist/",
|
||||
"/node_modules/",
|
||||
"/frontend/",
|
||||
"/website/"
|
||||
],
|
||||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json"],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
|
Loading…
Reference in New Issue
Block a user