diff --git a/.eslintrc.json b/.eslintrc.json
index 324e291c..9cbbd586 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -3,6 +3,7 @@
"browser": true,
"es2020": true,
"node": true,
+ "jquery": true,
"jest": true
},
"parser": "@typescript-eslint/parser",
@@ -14,6 +15,7 @@
"sourceType": "module"
},
"extends": [
+ "plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"eslint:recommended",
"plugin:prettier/recommended",
@@ -39,7 +41,8 @@
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/no-empty-interface": "off",
- "@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }]
+ "@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }],
+ "@typescript-eslint/no-namespace": ["error", { "allowDeclarations": true }]
},
"globals": {
"React": "writable"
diff --git a/cypress/e2e/api.cy.ts b/cypress/e2e/api.cy.ts
new file mode 100644
index 00000000..e69b5dff
--- /dev/null
+++ b/cypress/e2e/api.cy.ts
@@ -0,0 +1,29 @@
+describe('Website tests', () => {
+ Cypress.session.clearAllSavedSessions();
+
+ beforeEach(() => {
+ cy.login(Cypress.env('umami_user'), Cypress.env('umami_password'));
+ });
+
+ //let userId;
+
+ it('creates a user.', () => {
+ cy.fixture('users').then(data => {
+ const userPost = data.userPost;
+ cy.request({
+ method: 'POST',
+ url: '/api/users',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: Cypress.env('authorization'),
+ },
+ body: userPost,
+ }).then(response => {
+ //userId = response.body.id;
+ expect(response.status).to.eq(200);
+ expect(response.body).to.have.property('username', 'cypress1');
+ expect(response.body).to.have.property('role', 'User');
+ });
+ });
+ });
+});
diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/login.cy.ts
index 5831c81d..507b1b58 100644
--- a/cypress/e2e/login.cy.ts
+++ b/cypress/e2e/login.cy.ts
@@ -1,22 +1,36 @@
describe('Login tests', () => {
+ beforeEach(() => {
+ cy.visit('/login');
+ });
+
it(
'logs user in with correct credentials and logs user out',
{
defaultCommandTimeout: 10000,
},
() => {
- cy.visit('/login');
- cy.getDataTest('input-username').find('input').click();
- cy.getDataTest('input-username').find('input').type(Cypress.env('umami_user'), { delay: 50 });
- cy.getDataTest('input-password').find('input').click();
+ cy.getDataTest('input-username').find('input').as('inputUsername').click();
+ cy.get('@inputUsername').type(Cypress.env('umami_user'), { delay: 0 });
+ cy.get('@inputUsername').click();
cy.getDataTest('input-password')
.find('input')
- .type(Cypress.env('umami_password'), { delay: 50 });
+ .type(Cypress.env('umami_password'), { delay: 0 });
cy.getDataTest('button-submit').click();
cy.url().should('eq', Cypress.config().baseUrl + '/dashboard');
- cy.getDataTest('button-profile').click();
- cy.getDataTest('item-logout').click();
- cy.url().should('eq', Cypress.config().baseUrl + '/login');
+ cy.logout();
},
);
+
+ it('login with blank inputs or incorrect credentials', () => {
+ cy.getDataTest('button-submit').click();
+ cy.contains(/Required/i).should('be.visible');
+
+ cy.getDataTest('input-username').find('input').as('inputUsername');
+ cy.get('@inputUsername').click();
+ cy.get('@inputUsername').type(Cypress.env('umami_user'), { delay: 0 });
+ cy.get('@inputUsername').click();
+ cy.getDataTest('input-password').find('input').type('wrongpassword', { delay: 0 });
+ cy.getDataTest('button-submit').click();
+ cy.contains(/Incorrect username and\/or password./i).should('be.visible');
+ });
});
diff --git a/cypress/e2e/user.cy.ts b/cypress/e2e/user.cy.ts
new file mode 100644
index 00000000..9f432f16
--- /dev/null
+++ b/cypress/e2e/user.cy.ts
@@ -0,0 +1,65 @@
+describe('Website tests', () => {
+ Cypress.session.clearAllSavedSessions();
+
+ beforeEach(() => {
+ cy.login(Cypress.env('umami_user'), Cypress.env('umami_password'));
+ cy.visit('/settings/users');
+ });
+
+ it('Add a User', () => {
+ // add user
+ cy.contains(/Create user/i).should('be.visible');
+ cy.getDataTest('button-create-user').click();
+ cy.getDataTest('input-username').find('input').as('inputName').click();
+ cy.get('@inputName').type('Test-user', { delay: 0 });
+ cy.getDataTest('input-password').find('input').as('inputPassword').click();
+ cy.get('@inputPassword').type('testPasswordCypress', { delay: 0 });
+ cy.getDataTest('dropdown-role').click();
+ cy.getDataTest('dropdown-item-user').click();
+ cy.getDataTest('button-submit').click();
+ cy.get('td[label="Username"]').should('contain.text', 'Test-user');
+ cy.get('td[label="Role"]').should('contain.text', 'User');
+ });
+
+ it('Edit a User role and password', () => {
+ // edit user
+ cy.get('table tbody tr')
+ .contains('td', /Test-user/i)
+ .parent()
+ .within(() => {
+ cy.getDataTest('link-button-edit').click(); // Clicks the button inside the row
+ });
+ cy.getDataTest('input-password').find('input').as('inputPassword').click();
+ cy.get('@inputPassword').type('newPassword', { delay: 0 });
+ cy.getDataTest('dropdown-role').click();
+ cy.getDataTest('dropdown-item-viewOnly').click();
+ cy.getDataTest('button-submit').click();
+
+ cy.visit('/settings/users');
+ cy.get('table tbody tr')
+ .contains('td', /Test-user/i)
+ .parent()
+ .should('contain.text', 'View only');
+
+ cy.logout();
+ cy.url().should('eq', Cypress.config().baseUrl + '/login');
+ cy.getDataTest('input-username').find('input').as('inputUsername').click();
+ cy.get('@inputUsername').type('Test-user', { delay: 0 });
+ cy.get('@inputUsername').click();
+ cy.getDataTest('input-password').find('input').type('newPassword', { delay: 0 });
+ cy.getDataTest('button-submit').click();
+ cy.url().should('eq', Cypress.config().baseUrl + '/dashboard');
+ });
+
+ it('Delete a website', () => {
+ // delete user
+ cy.get('table tbody tr')
+ .contains('td', /Test-user/i)
+ .parent()
+ .within(() => {
+ cy.getDataTest('button-delete').click(); // Clicks the button inside the row
+ });
+ cy.contains(/Are you sure you want to delete Test-user?/i).should('be.visible');
+ cy.getDataTest('button-confirm').click();
+ });
+});
diff --git a/cypress/e2e/website.cy.ts b/cypress/e2e/website.cy.ts
index b60d8e7a..2dcd6027 100644
--- a/cypress/e2e/website.cy.ts
+++ b/cypress/e2e/website.cy.ts
@@ -10,10 +10,10 @@ describe('Website tests', () => {
cy.visit('/settings/websites');
cy.getDataTest('button-website-add').click();
cy.contains(/Add website/i).should('be.visible');
- cy.getDataTest('input-name').find('input').click();
- cy.getDataTest('input-name').find('input').type('Add test', { delay: 50 });
+ cy.getDataTest('input-name').find('input').as('inputUsername').click();
+ cy.getDataTest('input-name').find('input').type('Add test', { delay: 0 });
cy.getDataTest('input-domain').find('input').click();
- cy.getDataTest('input-domain').find('input').type('addtest.com', { delay: 50 });
+ cy.getDataTest('input-domain').find('input').type('addtest.com', { delay: 0 });
cy.getDataTest('button-submit').click();
cy.get('td[label="Name"]').should('contain.text', 'Add test');
cy.get('td[label="Domain"]').should('contain.text', 'addtest.com');
@@ -41,10 +41,10 @@ describe('Website tests', () => {
cy.contains(/Details/i).should('be.visible');
cy.getDataTest('input-name').find('input').click();
cy.getDataTest('input-name').find('input').clear();
- cy.getDataTest('input-name').find('input').type('Updated website', { delay: 50 });
+ cy.getDataTest('input-name').find('input').type('Updated website', { delay: 0 });
cy.getDataTest('input-domain').find('input').click();
cy.getDataTest('input-domain').find('input').clear();
- cy.getDataTest('input-domain').find('input').type('updatedwebsite.com', { delay: 50 });
+ cy.getDataTest('input-domain').find('input').type('updatedwebsite.com', { delay: 0 });
cy.getDataTest('button-submit').click({ force: true });
cy.getDataTest('input-name').find('input').should('have.value', 'Updated website');
cy.getDataTest('input-domain').find('input').should('have.value', 'updatedwebsite.com');
diff --git a/cypress/fixtures/users.json b/cypress/fixtures/users.json
new file mode 100644
index 00000000..420a71c3
--- /dev/null
+++ b/cypress/fixtures/users.json
@@ -0,0 +1,17 @@
+{
+ "userGet": {
+ "name": "cypress",
+ "email": "password",
+ "role": "User"
+ },
+ "userPost": {
+ "username": "cypress1",
+ "password": "password",
+ "role": "User"
+ },
+ "userDelete": {
+ "name": "Charlie",
+ "email": "charlie@example.com",
+ "age": 35
+ }
+}
diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts
index 2c45142b..a300b969 100644
--- a/cypress/support/e2e.ts
+++ b/cypress/support/e2e.ts
@@ -5,6 +5,12 @@ Cypress.Commands.add('getDataTest', (value: string) => {
return cy.get(`[data-test=${value}]`);
});
+Cypress.Commands.add('logout', () => {
+ cy.getDataTest('button-profile').click();
+ cy.getDataTest('item-logout').click();
+ cy.url().should('eq', Cypress.config().baseUrl + '/login');
+});
+
Cypress.Commands.add('login', (username: string, password: string) => {
cy.session([username, password], () => {
cy.request({
diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts
index 90cca19b..e89b24dd 100644
--- a/cypress/support/index.d.ts
+++ b/cypress/support/index.d.ts
@@ -1,4 +1,5 @@
///
+/* global JQuery */
declare namespace Cypress {
interface Chainable {
@@ -7,6 +8,11 @@ declare namespace Cypress {
* @example cy.getDataTest('greeting')
*/
getDataTest(value: string): Chainable>;
+ /**
+ * Custom command to logout through UI.
+ * @example cy.logout()
+ */
+ logout(): Chainable>;
/**
* Custom command to login user into the app.
* @example cy.login('admin', 'password)
diff --git a/src/app/(main)/settings/users/UserAddButton.tsx b/src/app/(main)/settings/users/UserAddButton.tsx
index e1b04842..674771b6 100644
--- a/src/app/(main)/settings/users/UserAddButton.tsx
+++ b/src/app/(main)/settings/users/UserAddButton.tsx
@@ -15,7 +15,7 @@ export function UserAddButton({ onSave }: { onSave?: () => void }) {
return (
-