Skip to content

Commit

Permalink
Merge pull request #2576 from umami-software/analytics
Browse files Browse the repository at this point in the history
v2.10.2
  • Loading branch information
mikecao authored Mar 7, 2024
2 parents 75d2e3a + 174404a commit a2245ef
Show file tree
Hide file tree
Showing 171 changed files with 3,327 additions and 673 deletions.
23 changes: 14 additions & 9 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@
"env": {
"browser": true,
"es2020": true,
"node": true
"node": true,
"jest": true
},
"extends": [
"eslint:recommended",
"plugin:prettier/recommended",
"plugin:import/recommended",
"plugin:@typescript-eslint/recommended",
"next"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
Expand All @@ -19,14 +13,25 @@
"ecmaVersion": 11,
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "prettier"],
"settings": {
"import/resolver": {
"node": {
"moduleDirectory": ["node_modules", "src/"]
}
}
},
"extends": [
"plugin:@typescript-eslint/recommended",
"eslint:recommended",
"plugin:prettier/recommended",
"plugin:import/errors",
"plugin:import/typescript",
"plugin:css-modules/recommended",
"plugin:cypress/recommended",
"prettier",
"next"
],
"plugins": ["@typescript-eslint", "prettier", "promise", "css-modules", "cypress"],
"rules": {
"no-console": "error",
"react/display-name": "off",
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache: 'yarn'
env:
DATABASE_TYPE: ${{ matrix.db-type }}
- run: npm install --global yarn
- run: yarn install --frozen-lockfile
- run: yarn install
- run: yarn test
- run: yarn build
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ yarn-error.log*
# local env files
.env
.env.*
*.env.*

*.dev.yml

8 changes: 1 addition & 7 deletions .stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@
"stylelint-config-prettier"
],
"rules": {
"no-descending-specificity": null,
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["global", "horizontal", "vertical"]
}
]
"no-descending-specificity": null
},
"ignoreFiles": ["**/*.js", "**/*.md"]
}
7 changes: 7 additions & 0 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'cypress';

export default defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
},
});
51 changes: 51 additions & 0 deletions cypress/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
version: '3'
services:
umami:
build: ../
#image: ghcr.io/umami-software/umami:postgresql-latest
ports:
- '3000:3000'
environment:
DATABASE_URL: postgresql://umami:umami@db:5432/umami
DATABASE_TYPE: postgresql
APP_SECRET: replace-me-with-a-random-string
depends_on:
db:
condition: service_healthy
restart: always
healthcheck:
test: ['CMD-SHELL', 'curl http://localhost:3000/api/heartbeat']
interval: 5s
timeout: 5s
retries: 5
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: umami
POSTGRES_USER: umami
POSTGRES_PASSWORD: umami
volumes:
- umami-db-data:/var/lib/postgresql/data
restart: always
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 5
cypress:
image: 'cypress/included:13.6.0'
depends_on:
- umami
- db
environment:
- CYPRESS_baseUrl=http://umami:3000
- CYPRESS_umami_user=admin
- CYPRESS_umami_password=umami
volumes:
- ../tsconfig.json:/tsconfig.json
- ../cypress.config.ts:/cypress.config.ts
- ./:/cypress
- ../node_modules/:/node_modules
volumes:
umami-db-data:
18 changes: 18 additions & 0 deletions cypress/e2e/login.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
describe('Login tests', () => {
it(
'logs user in with correct credentials and logs user out',
{
defaultCommandTimeout: 10000,
},
() => {
cy.visit('/login');
cy.getDataTest('input-username').find('input').type(Cypress.env('umami_user'));
cy.getDataTest('input-password').find('input').type(Cypress.env('umami_password'));
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');
},
);
});
91 changes: 91 additions & 0 deletions cypress/e2e/website.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
describe('Website tests', () => {
Cypress.session.clearAllSavedSessions();

beforeEach(() => {
cy.login(Cypress.env('umami_user'), Cypress.env('umami_password'));
});

it('Add a website', () => {
// add website
cy.visit('/settings/websites');
cy.getDataTest('button-website-add').click();
cy.contains(/Add website/i).should('be.visible');
cy.getDataTest('input-name').find('input').wait(500).type('Add test', { delay: 50 });
cy.getDataTest('input-domain').find('input').wait(500).type('addtest.com', { delay: 50 });
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');

// clean-up data
cy.getDataTest('link-button-edit').first().click();
cy.contains(/Details/i).should('be.visible');
cy.getDataTest('text-field-websiteId')
.find('input')
.then($input => {
const websiteId = $input[0].value;
cy.deleteWebsite(websiteId);
});
cy.visit('/settings/websites');
cy.contains('Add test').should('not.exist');
});

it.only('Edit a website', () => {
// prep data
cy.addWebsite('Update test', 'updatetest.com');
cy.visit('/settings/websites');

// edit website
cy.getDataTest('link-button-edit').first().click();
cy.contains(/Details/i).should('be.visible');
cy.getDataTest('input-name')
.find('input')
.wait(500)
.clear()
.type('Updated website', { delay: 50 });
cy.getDataTest('input-domain')
.find('input')
.wait(500)
.clear()
.type('updatedwebsite.com', { delay: 50 });
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');

// verify tracking script
cy.get('div')
.contains(/Tracking code/i)
.click();
cy.get('textarea').should('contain.text', Cypress.config().baseUrl + '/script.js');

// clean-up data
cy.get('div')
.contains(/Details/i)
.click();
cy.contains(/Details/i).should('be.visible');
cy.getDataTest('text-field-websiteId')
.find('input')
.then($input => {
const websiteId = $input[0].value;
cy.deleteWebsite(websiteId);
});
cy.visit('/settings/websites');
cy.contains('Add test').should('not.exist');
});

it('Delete a website', () => {
// prep data
cy.addWebsite('Delete test', 'deletetest.com');
cy.visit('/settings/websites');

// delete website
cy.getDataTest('link-button-edit').first().click();
cy.contains(/Data/i).should('be.visible');
cy.get('div').contains(/Data/i).click();
cy.contains(/All website data will be deleted./i).should('be.visible');
cy.getDataTest('button-delete').click();
cy.contains(/Type DELETE in the box below to confirm./i).should('be.visible');
cy.get('input[name="confirm"').type('DELETE');
cy.get('button[type="submit"]').click();
cy.contains('Delete test').should('not.exist');
});
});
57 changes: 57 additions & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/// <reference types="cypress" />
import { uuid } from '../../src/lib/crypto';

Cypress.Commands.add('getDataTest', (value: string) => {
return cy.get(`[data-test=${value}]`);
});

Cypress.Commands.add('login', (username: string, password: string) => {
cy.session([username, password], () => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: {
username,
password,
},
})
.then(response => {
Cypress.env('authorization', `bearer ${response.body.token}`);
window.localStorage.setItem('umami.auth', JSON.stringify(response.body.token));
})
.its('status')
.should('eq', 200);
});
});

Cypress.Commands.add('addWebsite', (name: string, domain: string) => {
cy.request({
method: 'POST',
url: '/api/websites',
headers: {
'Content-Type': 'application/json',
Authorization: Cypress.env('authorization'),
},
body: {
id: uuid(),
createdBy: '41e2b680-648e-4b09-bcd7-3e2b10c06264',
name: name,
domain: domain,
},
}).then(response => {
expect(response.status).to.eq(200);
});
});

Cypress.Commands.add('deleteWebsite', (websiteId: string) => {
cy.request({
method: 'DELETE',
url: `/api/websites/${websiteId}`,
headers: {
'Content-Type': 'application/json',
Authorization: Cypress.env('authorization'),
},
}).then(response => {
expect(response.status).to.eq(200);
});
});
26 changes: 26 additions & 0 deletions cypress/support/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/// <reference types="cypress" />

declare namespace Cypress {
interface Chainable {
/**
* Custom command to select DOM element by data-test attribute.
* @example cy.getDataTest('greeting')
*/
getDataTest(value: string): Chainable<JQuery<HTMLElement>>;
/**
* Custom command to login user into the app.
* @example cy.login('admin', 'password)
*/
login(username: string, password: string): Chainable<JQuery<HTMLElement>>;
/**
* Custom command to create a website
* @example cy.addWebsite('test', 'test.com')
*/
addWebsite(name: string, domain: string): Chainable<JQuery<HTMLElement>>;
/**
* Custom command to create a website
* @example cy.deleteWebsite('02d89813-7a72-41e1-87f0-8d668f85008b')
*/
deleteWebsite(websiteId: string): Chainable<JQuery<HTMLElement>>;
}
}
8 changes: 8 additions & 0 deletions cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "node"]
},
"include": ["**/*.ts", "../cypress.config.ts"]
}
7 changes: 7 additions & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
roots: ['./src'],
testMatch: ['**/__tests__/**/*.+(ts|tsx|js)', '**/?(*.)+(spec|test).+(ts|tsx|js)'],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
};
Loading

0 comments on commit a2245ef

Please sign in to comment.