mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-22 09:57:00 +01:00
Merge master.
This commit is contained in:
commit
9f90412b25
1
.gitignore
vendored
1
.gitignore
vendored
@ -35,6 +35,7 @@ yarn-error.log*
|
|||||||
# local env files
|
# local env files
|
||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
*.env.*
|
||||||
|
|
||||||
*.dev.yml
|
*.dev.yml
|
||||||
|
|
||||||
|
7
cypress.config.ts
Normal file
7
cypress.config.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'cypress';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
baseUrl: 'http://localhost:3000',
|
||||||
|
},
|
||||||
|
});
|
51
cypress/docker-compose.yml
Normal file
51
cypress/docker-compose.yml
Normal file
@ -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
cypress/e2e/login.cy.ts
Normal file
18
cypress/e2e/login.cy.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
describe('Login test', () => {
|
||||||
|
it(
|
||||||
|
'logs user in with correct credentials and logs user out',
|
||||||
|
{
|
||||||
|
defaultCommandTimeout: 10000,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
cy.visit('/login');
|
||||||
|
cy.dataCy('input-username').type(Cypress.env('umami_user'));
|
||||||
|
cy.dataCy('input-password').type(Cypress.env('umami_password'));
|
||||||
|
cy.dataCy('button-submit').click();
|
||||||
|
cy.url().should('eq', Cypress.config().baseUrl + '/dashboard');
|
||||||
|
cy.dataCy('button-profile').click();
|
||||||
|
cy.dataCy('item-logout').click();
|
||||||
|
cy.url().should('eq', Cypress.config().baseUrl + '/login');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
5
cypress/support/e2e.ts
Normal file
5
cypress/support/e2e.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
Cypress.Commands.add('dataCy', value => {
|
||||||
|
return cy.get(`[data-cy=${value}]`);
|
||||||
|
});
|
11
cypress/support/index.d.ts
vendored
Normal file
11
cypress/support/index.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
declare namespace Cypress {
|
||||||
|
interface Chainable {
|
||||||
|
/**
|
||||||
|
* Custom command to select DOM element by data-cy attribute.
|
||||||
|
* @example cy.dataCy('greeting')
|
||||||
|
*/
|
||||||
|
dataCy(value: string): Chainable<JQuery<HTMLElement>>;
|
||||||
|
}
|
||||||
|
}
|
8
cypress/tsconfig.json
Normal file
8
cypress/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es5", "dom"],
|
||||||
|
"types": ["cypress", "node"]
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts", "../cypress.config.ts"]
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "umami",
|
"name": "umami",
|
||||||
"version": "2.10.1",
|
"version": "2.11.0",
|
||||||
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
|
||||||
"author": "Umami Software, Inc. <hello@umami.is>",
|
"author": "Umami Software, Inc. <hello@umami.is>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -42,7 +42,9 @@
|
|||||||
"change-password": "node scripts/change-password.js",
|
"change-password": "node scripts/change-password.js",
|
||||||
"lint": "next lint --quiet",
|
"lint": "next lint --quiet",
|
||||||
"prepare": "node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky install",
|
"prepare": "node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky install",
|
||||||
"postbuild": "node scripts/postbuild.js"
|
"postbuild": "node scripts/postbuild.js",
|
||||||
|
"cypress-open": "cypress open cypress run",
|
||||||
|
"cypress-run": "cypress run cypress run"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"**/*.{js,jsx,ts,tsx}": [
|
"**/*.{js,jsx,ts,tsx}": [
|
||||||
@ -133,6 +135,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
"@typescript-eslint/eslint-plugin": "^6.7.3",
|
||||||
"@typescript-eslint/parser": "^6.7.3",
|
"@typescript-eslint/parser": "^6.7.3",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
"cypress": "^13.6.6",
|
||||||
"esbuild": "^0.17.17",
|
"esbuild": "^0.17.17",
|
||||||
"eslint": "^8.33.0",
|
"eslint": "^8.33.0",
|
||||||
"eslint-config-next": "^14.0.4",
|
"eslint-config-next": "^14.0.4",
|
||||||
|
@ -42,17 +42,30 @@ export function LoginForm() {
|
|||||||
<div className={styles.title}>umami</div>
|
<div className={styles.title}>umami</div>
|
||||||
<Form className={styles.form} onSubmit={handleSubmit} error={getMessage(error)}>
|
<Form className={styles.form} onSubmit={handleSubmit} error={getMessage(error)}>
|
||||||
<FormRow label={formatMessage(labels.username)}>
|
<FormRow label={formatMessage(labels.username)}>
|
||||||
<FormInput name="username" rules={{ required: formatMessage(labels.required) }}>
|
<FormInput
|
||||||
|
data-cy="input-username"
|
||||||
|
name="username"
|
||||||
|
rules={{ required: formatMessage(labels.required) }}
|
||||||
|
>
|
||||||
<TextField autoComplete="off" />
|
<TextField autoComplete="off" />
|
||||||
</FormInput>
|
</FormInput>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow label={formatMessage(labels.password)}>
|
<FormRow label={formatMessage(labels.password)}>
|
||||||
<FormInput name="password" rules={{ required: formatMessage(labels.required) }}>
|
<FormInput
|
||||||
|
data-cy="input-password"
|
||||||
|
name="password"
|
||||||
|
rules={{ required: formatMessage(labels.required) }}
|
||||||
|
>
|
||||||
<PasswordField />
|
<PasswordField />
|
||||||
</FormInput>
|
</FormInput>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormButtons>
|
<FormButtons>
|
||||||
<SubmitButton className={styles.button} variant="primary" disabled={isPending}>
|
<SubmitButton
|
||||||
|
data-cy="button-submit"
|
||||||
|
className={styles.button}
|
||||||
|
variant="primary"
|
||||||
|
disabled={isPending}
|
||||||
|
>
|
||||||
{formatMessage(labels.login)}
|
{formatMessage(labels.login)}
|
||||||
</SubmitButton>
|
</SubmitButton>
|
||||||
</FormButtons>
|
</FormButtons>
|
||||||
|
@ -25,7 +25,7 @@ export function ProfileButton() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PopupTrigger>
|
<PopupTrigger>
|
||||||
<Button variant="quiet">
|
<Button data-cy="button-profile" variant="quiet">
|
||||||
<Icon>
|
<Icon>
|
||||||
<Icons.Profile />
|
<Icons.Profile />
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -41,7 +41,7 @@ export function ProfileButton() {
|
|||||||
<Text>{formatMessage(labels.profile)}</Text>
|
<Text>{formatMessage(labels.profile)}</Text>
|
||||||
</Item>
|
</Item>
|
||||||
{!cloudMode && (
|
{!cloudMode && (
|
||||||
<Item key="logout" className={styles.item}>
|
<Item data-cy="item-logout" key="logout" className={styles.item}>
|
||||||
<Icon>
|
<Icon>
|
||||||
<Icons.Logout />
|
<Icons.Logout />
|
||||||
</Icon>
|
</Icon>
|
||||||
|
Loading…
Reference in New Issue
Block a user