Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,23 @@ jobs:
- name: Checkout
uses: actions/checkout@v5

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.5.0

- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Install dependencies
run: npm ci
run: pnpm install --frozen-lockfile

- name: Run unit tests
run: npm test -- --watch=false
run: pnpm test --watch=false

- name: Build production app
run: npm run build
run: pnpm run build
15 changes: 10 additions & 5 deletions .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,26 @@ jobs:
- name: Checkout
uses: actions/checkout@v5

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 11.5.0

- name: Setup Node
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
cache: pnpm
cache-dependency-path: pnpm-lock.yaml

- name: Install dependencies
run: npm ci
run: pnpm install --frozen-lockfile

- name: Run unit tests
run: npm test -- --watch=false
run: pnpm test --watch=false

- name: Build Angular app
run: npm run build -- --configuration production --base-href /${{ github.event.repository.name }}/
run: pnpm run build -- --configuration production --base-href /${{ github.event.repository.name }}/

- name: Add SPA fallback
run: cp dist/devbreak-timer/browser/index.html dist/devbreak-timer/browser/404.html
Expand Down
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"cli": {
"packageManager": "npm"
"packageManager": "pnpm"
},
"newProjectRoot": "projects",
"projects": {
Expand Down
12 changes: 6 additions & 6 deletions cypress/e2e/focus-workflow.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,24 @@ describe('Focus workflow', () => {

cy.getByTestId('start-button').click();
cy.tick(1_000);
cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
cy.getByTestId('timer-display').should('contain.text', '00:59');

cy.getByTestId('pause-button').click();
cy.getByTestId('timer-status').should('contain.text', 'paused');
cy.getByTestId('timer-status').should('contain.text', 'Paused');

cy.getByTestId('start-button').click();
cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');

cy.getByTestId('reset-button').click();
cy.getByTestId('timer-status').should('contain.text', 'idle');
cy.getByTestId('timer-status').should('contain.text', 'Ready');
cy.getByTestId('timer-display').should('contain.text', '01:00');

cy.getByTestId('start-button').click();
cy.tick(60_000);

cy.getByTestId('timer-status').should('contain.text', 'completed');
cy.contains('.focus-completion', 'Focus Session Completed').should('be.visible');
cy.getByTestId('timer-status').should('contain.text', 'Done');
cy.contains('.focus-completion', 'Focus complete').should('be.visible');
cy.contains('.focus-completion', 'Focus reliability pass').should('be.visible');
});
});
Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/kanban-workflow.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ describe('Kanban core workflow', () => {
});

it('creates, edits, moves, archives, and restores a task', () => {
cy.contains('label', 'New Task').find('input').type('Draft release notes');
cy.contains('label', 'New task').find('input').type('Draft release notes');
cy.contains('label', 'Description').find('textarea').type('Capture launch details');
cy.contains('button', 'Add Task').click();
cy.contains('button', 'Add task').click();

taskCard('Draft release notes').within(() => {
cy.contains('button', 'Edit').click();
Expand All @@ -29,7 +29,7 @@ describe('Kanban core workflow', () => {
cy.contains('button', 'Archive').click();
});

cy.contains('summary', 'Archived Tasks').click();
cy.contains('summary', 'Archived').click();
cy.get('.kanban-archive').within(() => {
cy.contains('Finalize release notes').should('be.visible');
cy.contains('button', 'Restore').click();
Expand Down
30 changes: 17 additions & 13 deletions cypress/e2e/keyboard-accessibility.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ describe('Keyboard and accessibility workflows', () => {
cy.press(Cypress.Keyboard.Keys.TAB);
cy.focused().should('contain.text', 'Active');

cy.contains('label', 'New Task').find('input').focus().type('Keyboard task');
cy.contains('button', 'Add Task').focus();
cy.contains('label', 'New task').find('input').focus().type('Keyboard task');
cy.contains('button', 'Add task').focus();
cy.focused().type('{enter}');
cy.getByTestId('kanban-column-ideas').should('contain.text', 'Keyboard task');

Expand All @@ -35,7 +35,7 @@ describe('Keyboard and accessibility workflows', () => {

cy.getByTestId('start-button').focus();
cy.focused().type('{enter}');
cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
});

it('restores focus after settings, quick-add, and task edit cancellation', () => {
Expand All @@ -50,10 +50,10 @@ describe('Keyboard and accessibility workflows', () => {
cy.focused().should('have.attr', 'data-testid', 'settings-button');

cy.getByTestId('kanban-column-todo').within(() => {
cy.contains('button', '+ Add task').click();
cy.contains('button', '+ Task').click();
cy.get('input[name="quickAddTitle"]').should('be.focused').type('Canceled task');
cy.focused().type('{esc}');
cy.focused().should('contain.text', '+ Add task');
cy.focused().should('contain.text', '+ Task');
});

cy.contains('[data-testid="task-card"]', 'Keyboard audit task').within(() => {
Expand All @@ -68,11 +68,11 @@ describe('Keyboard and accessibility workflows', () => {
cy.visit('/');

cy.getByTestId('settings-button').click();
cy.get('[aria-label="Edit shortcut for Search"]').click();
cy.get('[aria-label="Edit shortcut for Search"]').should('contain.text', 'Press keys');
shortcutButton('Search').click();
shortcutButton('Search').should('contain.text', 'Press keys');

cy.focused().type('{esc}');
cy.get('[aria-label="Edit shortcut for Search"]').should('not.contain.text', 'Press keys');
shortcutButton('Search').should('not.contain.text', 'Press keys');
cy.get('.settings-panel').should('be.visible');

cy.get('body').type('{esc}');
Expand All @@ -86,10 +86,10 @@ describe('Keyboard and accessibility workflows', () => {
cy.getByTestId('settings-button').click();
cy.focused().should('contain.text', 'Close');

cy.focused().type('{shift+tab}');
cy.focused().trigger('keydown', { key: 'Tab', shiftKey: true });
cy.focused().should('contain.text', 'Apply');

cy.focused().type('{tab}');
cy.focused().trigger('keydown', { key: 'Tab' });
cy.focused().should('contain.text', 'Close');

cy.get('body').type('{esc}');
Expand All @@ -106,8 +106,8 @@ describe('Keyboard and accessibility workflows', () => {
},
});

cy.contains('label', 'New Task').find('input').type('Reduced motion task');
cy.contains('button', 'Add Task').click();
cy.contains('label', 'New task').find('input').type('Reduced motion task');
cy.contains('button', 'Add task').click();
cy.getByTestId('kanban-column-ideas').should('contain.text', 'Reduced motion task');

cy.get('.kanban-density').contains('button', 'Compact').click();
Expand All @@ -117,10 +117,14 @@ describe('Keyboard and accessibility workflows', () => {

cy.getByTestId('start-button').click();
cy.tick(1_000);
cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
});
});

function shortcutButton(label: string): Cypress.Chainable<JQuery<HTMLButtonElement>> {
return cy.contains('.shortcut-row', label).find('button.shortcut-key');
}

function createTask() {
return {
id: 'task-keyboard-e2e',
Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/persistence-workflow.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ describe('Persistence restore workflow', () => {
},
});

cy.contains('label', 'New Task').find('input').type('Persistent task');
cy.contains('button', 'Add Task').click();
cy.contains('label', 'New task').find('input').type('Persistent task');
cy.contains('button', 'Add task').click();
cy.reload();

cy.getByTestId('kanban-column-ideas').should('contain.text', 'Persistent task');
Expand All @@ -30,7 +30,7 @@ describe('Persistence restore workflow', () => {
cy.reload();
cy.tick(2_000);

cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
cy.getByTestId('timer-display').should('contain.text', '04:58');
});

Expand Down
16 changes: 10 additions & 6 deletions cypress/e2e/timer.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('DevBreak Timer', () => {
cy.getByTestId('start-button').click();
cy.tick(1_000);

cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
cy.getByTestId('timer-display').should('contain.text', '24:59');
});

Expand All @@ -25,7 +25,7 @@ describe('DevBreak Timer', () => {
cy.reload();
cy.tick(2_000);

cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
cy.getByTestId('timer-display').should('contain.text', '04:58');
});

Expand All @@ -40,9 +40,11 @@ describe('DevBreak Timer', () => {
cy.tick(1_000);
cy.window().then((win) => win.dispatchEvent(new Event('focus')));

cy.getByTestId('session-title').should('contain.text', 'Short Break');
cy.getByTestId('pomodoro-session-panel')
.should('contain.text', 'Current')
.and('contain.text', 'Short Break');
cy.getByTestId('session-meta').should('contain.text', 'Cycle 2 / 4');
cy.getByTestId('timer-status').should('contain.text', 'running');
cy.getByTestId('timer-status').should('contain.text', 'Running');
cy.get('@notification').should('have.been.calledOnce');
});

Expand All @@ -51,7 +53,9 @@ describe('DevBreak Timer', () => {
cy.visit('/');

cy.getByTestId('pomodoro-toggle').click({ force: true });
cy.getByTestId('session-title').should('contain.text', 'Focus Session');
cy.getByTestId('pomodoro-session-panel')
.should('contain.text', 'Current')
.and('contain.text', 'Focus Session');
cy.document().its('documentElement.scrollWidth').should('be.lte', 320);
});

Expand All @@ -66,7 +70,7 @@ describe('DevBreak Timer', () => {
cy.tick(1_000);
cy.window().then((win) => win.dispatchEvent(new Event('focus')));

cy.getByTestId('timer-status').should('contain.text', 'completed');
cy.getByTestId('timer-status').should('contain.text', 'Done');
cy.get('@notification').should('not.have.been.called');
});
});
Expand Down
Loading
Loading