diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..d1132af --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,26 @@ +name: tests +on: + push: + branches: + - main + - renovate/* + pull_request: +jobs: + tests: + name: Tests + runs-on: docker + container: + image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:672aee9a5dfc35531db3a218ad9486eb5c5d7d9ac10bdcba13110470c10403ee + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + + - name: Enable corepack + run: corepack enable + + - name: Cypress run + uses: cypress-io/github-action@v6 + with: + build: yarn run build + start: yarn start + component: true diff --git a/cypress.config.ts b/cypress.config.ts index 768b87a..d2013c5 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,12 +1,6 @@ import { defineConfig } from 'cypress'; export default defineConfig({ - e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, - }, - component: { devServer: { framework: 'next', diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 95857ae..59717f5 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -35,3 +35,28 @@ // } // } // } + +Cypress.Commands.add('getBySel', (selector, ...args) => { + return cy.get(`[data-cy=${selector}]`, ...args); +}); + +Cypress.Commands.add('getBySelLike', (selector, ...args) => { + return cy.get(`[data-cy*=${selector}]`, ...args); +}); + +declare global { + namespace Cypress { + interface Chainable { + getBySel( + selector: string, + ...args: any[] + ): Chainable>; + getBySelLike( + selector: string, + ...args: any[] + ): Chainable>; + } + } +} + +export {}; diff --git a/cypress/support/component.ts b/cypress/support/component.ts index 23fbccc..b1f1c92 100644 --- a/cypress/support/component.ts +++ b/cypress/support/component.ts @@ -13,6 +13,8 @@ // https://on.cypress.io/configuration // *********************************************************** +import '@/app/globals.css'; + // Import commands.js using ES2015 syntax: import './commands'; diff --git a/package.json b/package.json index e3bcfc4..a9fa0b1 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,7 @@ "lint": "next lint", "format": "prettier --write .", "cypress:open": "cypress open", - "cypress:run": "cypress run", - "test": "start-server-and-test " + "cypress:run": "cypress run" }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.7.2", diff --git a/src/components/icon-button.cy.tsx b/src/components/icon-button.cy.tsx new file mode 100644 index 0000000..b5a1ff0 --- /dev/null +++ b/src/components/icon-button.cy.tsx @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +import React from 'react'; +import { IconButton } from './icon-button'; +import { faOpenid } from '@fortawesome/free-brands-svg-icons'; + +describe('', () => { + it('renders', () => { + cy.mount(Button); + }); + + it('is clickable', () => { + const onClick = cy.stub(); + cy.mount( + + Button + , + ); + cy.getBySel('icon-button') + .click() + .then(() => { + expect(onClick).to.be.calledOnce; + }); + }); +}); diff --git a/src/components/user/theme-picker.cy.tsx b/src/components/user/theme-picker.cy.tsx new file mode 100644 index 0000000..23dc01b --- /dev/null +++ b/src/components/user/theme-picker.cy.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { ThemePicker } from '@/components/user/theme-picker'; +import { ThemeProvider } from '../theme-provider'; + +describe('', () => { + it('renders', () => { + cy.mount(); + }); + + it('toggle open and close', () => { + cy.mount(); + cy.getBySel('theme-picker').click(); + cy.getBySel('theme-picker-content').should('exist'); + cy.get('html').click(); + cy.getBySel('theme-picker-content').should('not.exist'); + }); + + it('enable dark mode', () => { + cy.mount( + + + , + ); + + cy.getBySel('theme-picker').click(); + cy.getBySel('dark-theme').click(); + cy.get('html').should('have.attr', 'data-theme', 'dark'); + }); + + it('enable light mode', () => { + cy.mount( + + + , + ); + + cy.getBySel('theme-picker').click(); + cy.getBySel('light-theme').click(); + cy.get('html').should('have.attr', 'data-theme', 'light'); + }); +}); diff --git a/src/components/user/theme-picker.tsx b/src/components/user/theme-picker.tsx index 5341c3c..3ceca4c 100644 --- a/src/components/user/theme-picker.tsx +++ b/src/components/user/theme-picker.tsx @@ -18,20 +18,26 @@ export function ThemePicker() { return ( - - - setTheme('light')}> + + setTheme('light')} + data-cy='light-theme' + > Light - setTheme('dark')}> + setTheme('dark')} data-cy='dark-theme'> Dark - setTheme('system')}> + setTheme('system')} + data-cy='system-theme' + > System diff --git a/tsconfig.json b/tsconfig.json index c133409..6bd88b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ ], "paths": { "@/*": ["./src/*"] - } + }, + "types": ["node", "cypress"] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"]