feat: add new token page e2e test (#330)

This commit is contained in:
Zhaoxinxin 2023-12-06 11:58:02 +08:00 committed by GitHub
parent 4d95cfb800
commit 1699523c97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 659 additions and 10 deletions

View File

@ -5,7 +5,7 @@ import user from '../fixtures/api/user.json';
import guestUser from '../fixtures/api/guest-user.json';
import seedPeers from '../fixtures/api/clusters/seed-peers.json';
import schedulers from '../fixtures/api/clusters/schedulers.json';
import createClustes from '../fixtures/api/clusters/create-cluster.json';
import createCluster from '../fixtures/api/clusters/create-cluster.json';
import _ from 'lodash';
describe('Create cluster', () => {
@ -141,7 +141,7 @@ describe('Create cluster', () => {
(req) => {
req.reply({
statusCode: 200,
body: createClustes,
body: createCluster,
});
},
);
@ -313,7 +313,7 @@ describe('Create cluster', () => {
cy.url().should('include', '/clusters/new');
cy.get('#name').clear();
// Enter the correct name
// Enter the correct name.
cy.get('#name').type('cluster-12');
cy.get('#name-helper-text').should('not.exist');

View File

@ -0,0 +1,319 @@
import root from '../fixtures/api/role-root.json';
import guest from '../fixtures/api/role-guest.json';
import user from '../fixtures/api/user.json';
import guestUser from '../fixtures/api/guest-user.json';
import tokens from '../fixtures/api/tokens/tokens.json';
import createToken from '../fixtures/api/tokens/create-token.json';
import _ from 'lodash';
describe('Create token', () => {
beforeEach(() => {
cy.signin();
cy.intercept(
{
method: 'GET',
url: '/api/v1/users/1',
},
(req) => {
req.reply({
statusCode: 200,
body: user,
});
},
);
cy.intercept(
{
method: 'GET',
url: '/api/v1/users/1/roles',
},
(req) => {
req.reply({
statusCode: 200,
body: root,
});
},
);
cy.intercept(
{
method: 'GET',
url: '/api/v1/personal-access-tokens?page=1&per_page=10000000',
},
(req) => {
req.reply({
statusCode: 200,
body: tokens,
});
},
);
cy.visit('/developer/personal-access-tokens/new');
cy.viewport(1440, 1080);
});
it('can create token', () => {
cy.visit('/developer/personal-access-tokens');
// Click the `ADD PERSONAL ACCESS TOKENS` button.
cy.get('.css-1qdyvok > .MuiButtonBase-root').click();
// Then I see that the current page is the developer/personal-access-tokens/new!
cy.url().should('include', '/developer/personal-access-tokens/new');
cy.get('#name').type('root-12');
// Choose an expiration time of 60 days.
cy.get('#demo-simple-select').click();
cy.get('[data-value="60"]').click();
cy.intercept(
{
method: 'POST',
url: '/api/v1/personal-access-tokens',
},
(req) => {
req.body = {};
req.reply({
statusCode: 200,
body: {
id: 12,
created_at: '2023-12-05T11:26:32Z',
updated_at: '2023-12-05T11:26:32Z',
is_del: 0,
name: 'root-12',
bio: '',
token: 'ZjM1NzM1NGItYjYwYi00OTEyLTlmN2QtNjc5M2JhNzhiOTI3',
scopes: [],
state: 'active',
expired_at: '2033-12-02T11:26:17Z',
user_id: 2,
},
});
},
);
cy.intercept(
{
method: 'GET',
url: '/api/v1/personal-access-tokens?page=1&per_page=10000000',
},
(req) => {
req.reply({
statusCode: 200,
body: createToken,
});
},
);
cy.get('#save').click();
// Then I see that the current page is the developer/personal-access-tokens!
cy.url().should('include', '/developer/personal-access-tokens');
// After creation is completed, the copyable token will be displayed.
cy.get('.css-1s0a73l').should('exist');
// Click the Copy icon button.
cy.get('.css-1s0a73l > .MuiButtonBase-root').click();
// The copy icon is no longer displayed.
cy.get('#copy').should('not.exist');
// Shwo done icon.
cy.get('#done').should('exist');
// Show `copied!` text.
cy.get('.MuiTooltip-tooltip').should('exist').and('have.text', 'copied!');
// Let's check the copied text.
cy.window()
.its('navigator.clipboard')
.then((clip) => clip.readText())
.should('equal', 'ZjM1NzM1NGItYjYwYi00OTEyLTlmN2QtNjc5M2JhNzhiOTI3');
cy.wait(1000);
cy.get('#copy').should('exist');
cy.get('#done').should('not.exist');
cy.get('.MuiTooltip-tooltip').should('not.exist');
// Display successfully created token information.
cy.get('#root-12').should('be.visible').and('have.text', 'root-12');
// Refresh page.
cy.reload().then(() => {
cy.wait(2000);
});
// When you click refresh, the replication token will not be displayed again for your security.
cy.get('.css-1s0a73l').should('not.exist');
});
it('cannot create token with existing token', () => {
cy.intercept(
{
method: 'POST',
url: '/api/v1/personal-access-tokens',
},
(req) => {
req.body = {};
req.reply({
statusCode: 409,
body: { message: 'Conflict' },
});
},
);
cy.get('#name').type('root-12{enter}');
// Show error message.
cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Conflict');
cy.get('.MuiAlert-action > .MuiButtonBase-root').click();
cy.get('.MuiPaper-root').should('not.exist');
});
it('click the `CANCEL button', () => {
cy.get('#cancel').click();
// Then I see that the current page is the developer/personal-access-tokens!
cy.url().should('include', '/developer/personal-access-tokens');
});
it('token cannot be created without required attributes', () => {
cy.get('#save').click();
cy.get('#name-helper-text').should('exist').and('have.text', 'Fill in the characters, the length is 1-100.');
});
it('try to create token with guest user', () => {
cy.visit('/signin');
cy.guestSignin();
cy.intercept(
{
method: 'GET',
url: '/api/v1/users/2',
},
(req) => {
req.reply({
statusCode: 200,
body: guestUser,
});
},
);
cy.intercept(
{
method: 'GET',
url: '/api/v1/users/2/roles',
},
(req) => {
req.reply({
statusCode: 200,
body: guest,
});
},
);
cy.intercept(
{
method: 'POST',
url: '/api/v1/personal-access-tokens',
},
(req) => {
req.body = {};
req.reply({
statusCode: 401,
body: { message: 'permission deny' },
});
},
);
cy.visit('/developer/personal-access-tokens/new');
// Users menu does not exist.
cy.get('.MuiSnackbar-root > .MuiPaper-root').should('not.exist');
cy.get('#name').type('root-12{enter}');
// Show error message.
cy.get('.MuiAlert-message').should('be.visible').and('contain', 'permission deny');
});
it('should handle API error response', () => {
cy.intercept(
{
method: 'POST',
url: '/api/v1/personal-access-tokens',
},
(req) => {
req.body = {};
req.reply({
forceNetworkError: true,
});
},
);
cy.get('#name').type('root-12{enter}');
// Show error message.
cy.get('.MuiAlert-message').should('be.visible').and('contain', 'Failed to fetch');
});
it('cannot create token with invalid attributes', () => {
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const name = _.times(101, () => _.sample(characters)).join('');
const description = _.times(1001, () => _.sample(characters)).join('');
// Should name message describing the validation error.
cy.get('#name').type(name);
// Show verification error message.
cy.get('#name-helper-text').should('exist').and('have.text', 'Fill in the characters, the length is 1-100.');
// Submit form when validation fails.
cy.get('#save').click();
// Then I see that the current page is the developer/personal-access-tokens/new!
cy.url().should('include', '/developer/personal-access-tokens/new');
cy.get('#name').clear();
// Enter the correct name.
cy.get('#name').type('root-12');
// Should display message describing the validation error.
cy.get('#bio').type(description);
// Show verification error message.
cy.get('#bio-helper-text').should('exist').and('have.text', 'Fill in the characters, the length is 0-1000.');
cy.get('#save').click();
// Then I see that the current page is the developer/personal-access-tokens/new!
cy.url().should('include', '/developer/personal-access-tokens/new');
cy.get(
':nth-child(1) > .MuiBox-root > .MuiFormControlLabel-root > .MuiButtonBase-root > .PrivateSwitchBase-input',
).click();
// Check if the preheat checkbox is checked.
cy.get(':nth-child(1) > .MuiBox-root > .MuiFormControlLabel-root > .MuiButtonBase-root > .PrivateSwitchBase-input')
.should('be.checked')
.check({ force: true });
cy.get(
':nth-child(2) > .MuiBox-root > .MuiFormControlLabel-root > .MuiButtonBase-root > .PrivateSwitchBase-input',
).click();
// Check if the job checkbox is checked.
cy.get(':nth-child(2) > .MuiBox-root > .MuiFormControlLabel-root > .MuiButtonBase-root > .PrivateSwitchBase-input')
.should('be.checked')
.check({ force: true });
cy.get(
':nth-child(3) > .MuiBox-root > .MuiFormControlLabel-root > .MuiButtonBase-root > .PrivateSwitchBase-input',
).click();
// Check if the cluster checkbox is checked.
cy.get(':nth-child(3) > .MuiBox-root > .MuiFormControlLabel-root > .MuiButtonBase-root > .PrivateSwitchBase-input')
.should('be.checked')
.check({ force: true });
});
});

View File

@ -2,7 +2,7 @@ import root from '../fixtures/api/role-root.json';
import user from '../fixtures/api/user.json';
import tokens from '../fixtures/api/tokens/tokens.json';
import deleteToken from '../fixtures/api/tokens/delete-tokens.json';
import deleteTokenAfter from '../fixtures/api/tokens/token-delete-after.json';
import tokenDeleteAfter from '../fixtures/api/tokens/token-delete-after.json';
describe('Tokens', () => {
beforeEach(() => {
@ -262,7 +262,7 @@ describe('Tokens', () => {
(req) => {
req.reply({
statusCode: 200,
body: deleteTokenAfter,
body: tokenDeleteAfter,
});
},
).as('delete');

View File

@ -0,0 +1,326 @@
[
{
"id": 1,
"created_at": "2023-12-04T10:27:17Z",
"updated_at": "2023-12-04T10:34:12Z",
"is_del": 0,
"name": "root-1",
"bio": "root-1 token,it's used for preheating of harbor.",
"token": "MTk3OTY5NTMtZjlkZi00YWVhLWI5OGEtZTc0YWVhMWZmYmQz",
"scopes": ["preheat"],
"state": "active",
"expired_at": "2033-12-01T10:34:08Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 2,
"created_at": "2023-12-04T10:39:31Z",
"updated_at": "2023-12-04T10:39:31Z",
"is_del": 0,
"name": "root-2",
"bio": "root-2 token, used to open API call warm-up job.",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["job"],
"state": "active",
"expired_at": "2024-08-11T10:37:53Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 3,
"created_at": "2023-12-04T10:41:31Z",
"updated_at": "2023-12-04T10:41:31Z",
"is_del": 0,
"name": "root-3",
"bio": "root-3 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2025-02-21T11:37:53Z",
"user_id": 1,
"user": {
"id": 1,
"created_at": "2023-11-06T06:09:04Z",
"updated_at": "2023-11-06T06:09:04Z",
"is_del": 0,
"email": "lucy@example.com",
"name": "lucy",
"avatar": "https://example.com/avatar.png",
"phone": "1234567890",
"state": "enable",
"location": "Hangzhou",
"bio": "I am lucy",
"configs": null
}
},
{
"id": 4,
"created_at": "2023-12-04T10:42:31Z",
"updated_at": "2023-12-04T10:42:31Z",
"is_del": 0,
"name": "root-4",
"bio": "root-4 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2024-02-11T10:37:53Z",
"user_id": 1,
"user": {
"id": 1,
"created_at": "2023-11-06T06:09:04Z",
"updated_at": "2023-11-06T06:09:04Z",
"is_del": 0,
"email": "lucy@example.com",
"name": "lucy",
"avatar": "https://example.com/avatar.png",
"phone": "1234567890",
"state": "enable",
"location": "Hangzhou",
"bio": "I am lucy",
"configs": null
}
},
{
"id": 5,
"created_at": "2023-12-04T10:43:31Z",
"updated_at": "2023-12-04T10:43:31Z",
"is_del": 0,
"name": "root-5",
"bio": "root-5 token, used to open API call warm-up job.",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["job"],
"state": "active",
"expired_at": "2027-09-14T10:37:53Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 6,
"created_at": "2023-12-04T10:45:31Z",
"updated_at": "2023-12-04T10:45:31Z",
"is_del": 0,
"name": "root-6",
"bio": "root-6 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2024-01-11T10:37:53Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 7,
"created_at": "2023-12-04T11:41:31Z",
"updated_at": "2023-12-04T11:41:31Z",
"is_del": 0,
"name": "root-7",
"bio": "root-7 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2025-06-11T10:37:53Z",
"user_id": 2,
"user": {
"id": 2,
"created_at": "2023-11-07T06:09:04Z",
"updated_at": "2023-11-07T06:09:04Z",
"is_del": 0,
"email": "jack@example.com",
"name": "jack",
"avatar": "https://example.com/avatar.png",
"phone": "1234567890",
"state": "enable",
"location": "Shanghai",
"bio": "I am jack",
"configs": null
}
},
{
"id": 8,
"created_at": "2023-12-04T11:49:31Z",
"updated_at": "2023-12-04T11:49:31Z",
"is_del": 0,
"name": "root-8",
"bio": "root-8 token,it's used for preheating of harbor.",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2025-01-11T10:37:53Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 9,
"created_at": "2023-12-05T10:41:31Z",
"updated_at": "2023-12-05T10:41:31Z",
"is_del": 0,
"name": "root-9",
"bio": "root-9 token,it's used for preheating of harbor.",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["preheat"],
"state": "active",
"expired_at": "2033-12-01T10:34:08Z",
"user_id": 2,
"user": {
"id": 2,
"created_at": "2023-11-07T06:09:04Z",
"updated_at": "2023-11-07T06:09:04Z",
"is_del": 0,
"email": "jack@example.com",
"name": "jack",
"avatar": "https://example.com/avatar.png",
"phone": "1234567890",
"state": "enable",
"location": "Shanghai",
"bio": "I am jack",
"configs": null
}
},
{
"id": 10,
"created_at": "2023-12-05T18:41:31Z",
"updated_at": "2023-12-05T18:41:31Z",
"is_del": 0,
"name": "root-10",
"bio": "root-10 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2024-12-1T10:37:53Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 11,
"created_at": "2023-12-06T10:41:31Z",
"updated_at": "2023-12-06T10:41:31Z",
"is_del": 0,
"name": "root-11",
"bio": "root-11 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": ["cluster"],
"state": "active",
"expired_at": "2024-12-11T10:37:53Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
},
{
"id": 12,
"created_at": "2023-12-06T11:41:31Z",
"updated_at": "2023-12-06T11:41:31Z",
"is_del": 0,
"name": "root-12",
"bio": "root-12 token, used to control of cluster",
"token": "YmVmYjA1Y2MtMmFkYy00OTJjLTg4OWUtYzg3MjE0ZWEyOWY4",
"scopes": [],
"state": "active",
"expired_at": "2024-02-03T11:36:05Z",
"user_id": 3,
"user": {
"id": 3,
"created_at": "2023-11-27T07:31:23Z",
"updated_at": "2023-11-27T07:31:23Z",
"is_del": 0,
"email": "root@example.com",
"name": "root",
"avatar": "",
"phone": "1234567890",
"state": "enable",
"location": "Guangzhou",
"bio": "This is root",
"configs": null
}
}
]

View File

@ -216,19 +216,21 @@ export default function PersonalAccessTokens() {
PopperProps={{
disablePortal: true,
}}
onClose={() => {
setShowCopyIcon(false);
}}
open={showCopyIcon}
disableFocusListener
disableHoverListener
disableTouchListener
title="copied!"
>
<Box component="img" sx={{ width: '1.2rem', height: '1.2rem' }} src="/icons/tokens/done.svg" />
<Box
component="img"
id="done"
sx={{ width: '1.2rem', height: '1.2rem' }}
src="/icons/tokens/done.svg"
/>
</Tooltip>
) : (
<Box component="img" sx={{ width: '1.2rem', height: '1.2rem' }} src="/icons/tokens/copy.svg" />
<Box component="img" id="copy" sx={{ width: '1.2rem', height: '1.2rem' }} src="/icons/tokens/copy.svg" />
)}
</IconButton>
</Box>

View File

@ -361,6 +361,7 @@ export default function CreateTokens() {
size="small"
variant="outlined"
loadingPosition="end"
id="cancel"
sx={{
'&.MuiLoadingButton-root': {
color: 'var(--calcel-size-color)',
@ -392,6 +393,7 @@ export default function CreateTokens() {
variant="outlined"
type="submit"
loadingPosition="end"
id="save"
sx={{
'&.MuiLoadingButton-root': {
backgroundColor: 'var(--save-color)',