diff --git a/.github/workflows/npm-format-check.yml b/.github/workflows/npm-format-check.yml new file mode 100644 index 00000000..088e5211 --- /dev/null +++ b/.github/workflows/npm-format-check.yml @@ -0,0 +1,23 @@ +name: NPM Format Check + +on: [push] + +jobs: + format-check: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Use Node.js" + uses: actions/setup-node@v4 + with: + node-version: '20.x' + + - name: "Install npm dependencies" + run: npm ci + + - name: "Check code formatting" + run: npm run format:check \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..6157e53f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,27 @@ +# Ignore build outputs +node_modules/ +vendor/ +storage/ +bootstrap/cache/ +public/build/ +public/hot/ + +# Ignore lock files +package-lock.json +composer.lock + +# Ignore generated files +*.min.js +*.min.css + +# Ignore test results +test-results/ +playwright-report/ + +# Ignore IDE files +.idea/ +.vscode/ + +# Ignore OS files +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json index 62762f5c..d689166c 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -3,5 +3,6 @@ "tabWidth": 4, "singleQuote": true, "bracketSameLine": true, - "quoteProps": "preserve" + "quoteProps": "preserve", + "printWidth": 100 } diff --git a/e2e/clients.spec.ts b/e2e/clients.spec.ts index 9ca98d5d..dab9c81f 100644 --- a/e2e/clients.spec.ts +++ b/e2e/clients.spec.ts @@ -7,11 +7,8 @@ async function goToProjectsOverview(page: Page) { } // Create new project via modal -test('test that creating and deleting a new client via the modal works', async ({ - page, -}) => { - const newClientName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); +test('test that creating and deleting a new client via the modal works', async ({ page }) => { + const newClientName = 'New Project ' + Math.floor(1 + Math.random() * 10000); await goToProjectsOverview(page); await page.getByRole('button', { name: 'Create Client' }).click(); await page.getByPlaceholder('Client Name').fill(newClientName); @@ -28,13 +25,9 @@ test('test that creating and deleting a new client via the modal works', async ( ]); await expect(page.getByTestId('client_table')).toContainText(newClientName); - const moreButton = page.locator( - "[aria-label='Actions for Client " + newClientName + "']" - ); + const moreButton = page.locator("[aria-label='Actions for Client " + newClientName + "']"); moreButton.click(); - const deleteButton = page.locator( - "[aria-label='Delete Client " + newClientName + "']" - ); + const deleteButton = page.locator("[aria-label='Delete Client " + newClientName + "']"); await Promise.all([ deleteButton.click(), @@ -45,9 +38,7 @@ test('test that creating and deleting a new client via the modal works', async ( response.status() === 204 ), ]); - await expect(page.getByTestId('client_table')).not.toContainText( - newClientName - ); + await expect(page.getByTestId('client_table')).not.toContainText(newClientName); }); test('test that archiving and unarchiving clients works', async ({ page }) => { diff --git a/e2e/members.spec.ts b/e2e/members.spec.ts index 253acf79..70be0e7c 100644 --- a/e2e/members.spec.ts +++ b/e2e/members.spec.ts @@ -22,12 +22,8 @@ test('test that new manager can be invited', async ({ page }) => { await page.getByLabel('Email').fill(`new+${editorId}@editor.test`); await page.getByRole('button', { name: 'Manager' }).click(); await Promise.all([ - page - .getByRole('button', { name: 'Invite Member', exact: true }) - .click(), - expect(page.getByRole('main')).toContainText( - `new+${editorId}@editor.test` - ), + page.getByRole('button', { name: 'Invite Member', exact: true }).click(), + expect(page.getByRole('main')).toContainText(`new+${editorId}@editor.test`), ]); }); @@ -38,12 +34,8 @@ test('test that new employee can be invited', async ({ page }) => { await page.getByLabel('Email').fill(`new+${editorId}@editor.test`); await page.getByRole('button', { name: 'Employee' }).click(); await Promise.all([ - page - .getByRole('button', { name: 'Invite Member', exact: true }) - .click(), - await expect(page.getByRole('main')).toContainText( - `new+${editorId}@editor.test` - ), + page.getByRole('button', { name: 'Invite Member', exact: true }).click(), + await expect(page.getByRole('main')).toContainText(`new+${editorId}@editor.test`), ]); }); @@ -54,12 +46,8 @@ test('test that new admin can be invited', async ({ page }) => { await page.getByLabel('Email').fill(`new+${adminId}@admin.test`); await page.getByRole('button', { name: 'Administrator' }).click(); await Promise.all([ - page - .getByRole('button', { name: 'Invite Member', exact: true }) - .click(), - expect(page.getByRole('main')).toContainText( - `new+${adminId}@admin.test` - ), + page.getByRole('button', { name: 'Invite Member', exact: true }).click(), + expect(page.getByRole('main')).toContainText(`new+${adminId}@admin.test`), ]); }); test('test that error shows if no role is selected', async ({ page }) => { @@ -69,9 +57,7 @@ test('test that error shows if no role is selected', async ({ page }) => { await page.getByLabel('Email').fill(`new+${noRoleId}@norole.test`); await Promise.all([ - page - .getByRole('button', { name: 'Invite Member', exact: true }) - .click(), + page.getByRole('button', { name: 'Invite Member', exact: true }).click(), expect(page.getByText('Please select a role')).toBeVisible(), ]); }); @@ -85,9 +71,7 @@ test('test that organization billable rate can be updated with all existing time await page.getByRole('menuitem').getByText('Edit').click(); await page.getByText('Organization Default Rate').click(); await page.getByText('Custom Rate').click(); - await page - .getByPlaceholder('Billable Rate') - .fill(newBillableRate.toString()); + await page.getByPlaceholder('Billable Rate').fill(newBillableRate.toString()); await page.getByRole('button', { name: 'Update Member' }).click(); await Promise.all([ @@ -103,8 +87,7 @@ test('test that organization billable rate can be updated with all existing time response.url().includes('/organizations/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.billable_rate === - newBillableRate * 100 + (await response.json()).data.billable_rate === newBillableRate * 100 ), ]); }); diff --git a/e2e/organization.spec.ts b/e2e/organization.spec.ts index ebb4a539..d2386577 100644 --- a/e2e/organization.spec.ts +++ b/e2e/organization.spec.ts @@ -35,9 +35,9 @@ test('test that organization name can be updated', async ({ page }) => { await page.getByLabel('Organization Name').fill('NEW ORG NAME'); await page.getByLabel('Organization Name').press('Enter'); await page.getByLabel('Organization Name').press('Meta+r'); - await expect( - page.locator('[data-testid="organization_switcher"]:visible') - ).toContainText('NEW ORG NAME'); + await expect(page.locator('[data-testid="organization_switcher"]:visible')).toContainText( + 'NEW ORG NAME' + ); }); test('test that organization billable rate can be updated with all existing time entries', async ({ @@ -46,9 +46,7 @@ test('test that organization billable rate can be updated with all existing time await goToOrganizationSettings(page); const newBillableRate = Math.round(Math.random() * 10000); await page.getByLabel('Organization Billable Rate').click(); - await page - .getByLabel('Organization Billable Rate') - .fill(newBillableRate.toString()); + await page.getByLabel('Organization Billable Rate').fill(newBillableRate.toString()); await page .locator('form') .filter({ hasText: 'Organization Billable' }) @@ -56,9 +54,7 @@ test('test that organization billable rate can be updated with all existing time .click(); await Promise.all([ - page - .getByRole('button', { name: 'Yes, update existing time entries' }) - .click(), + page.getByRole('button', { name: 'Yes, update existing time entries' }).click(), page.waitForRequest( async (request) => request.url().includes('/organizations/') && @@ -70,15 +66,12 @@ test('test that organization billable rate can be updated with all existing time response.url().includes('/organizations/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.billable_rate === - newBillableRate * 100 + (await response.json()).data.billable_rate === newBillableRate * 100 ), ]); }); -test('test that organization format settings can be updated', async ({ - page, -}) => { +test('test that organization format settings can be updated', async ({ page }) => { await goToOrganizationSettings(page); // Test number format @@ -113,8 +106,7 @@ test('test that organization format settings can be updated', async ({ response.url().includes('/organizations/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.currency_format === - 'iso-code-after-with-space' + (await response.json()).data.currency_format === 'iso-code-after-with-space' ), ]); @@ -132,8 +124,7 @@ test('test that organization format settings can be updated', async ({ response.url().includes('/organizations/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.date_format === - 'slash-separated-dd-mm-yyyy' + (await response.json()).data.date_format === 'slash-separated-dd-mm-yyyy' ), ]); @@ -169,19 +160,14 @@ test('test that organization format settings can be updated', async ({ response.url().includes('/organizations/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.interval_format === - 'hours-minutes-colon-separated' + (await response.json()).data.interval_format === 'hours-minutes-colon-separated' ), ]); }); -test('test that format settings are reflected in the dashboard', async ({ - page, -}) => { +test('test that format settings are reflected in the dashboard', async ({ page }) => { // check that 0h 00min is displayed - await expect( - page.getByText('0h 00min', { exact: true }).nth(0) - ).toBeVisible(); + await expect(page.getByText('0h 00min', { exact: true }).nth(0)).toBeVisible(); // First set the format settings await goToOrganizationSettings(page); @@ -213,10 +199,8 @@ test('test that format settings are reflected in the dashboard', async ({ response.url().includes('/organizations/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.interval_format === - 'hours-minutes-colon-separated' && - (await response.json()).data.currency_format === - 'symbol-after' && + (await response.json()).data.interval_format === 'hours-minutes-colon-separated' && + (await response.json()).data.currency_format === 'symbol-after' && (await response.json()).data.number_format === 'comma-point' ), ]); @@ -232,16 +216,12 @@ test('test that format settings are reflected in the dashboard', async ({ // check that 00:00 is displayed await expect(page.getByText('0:00', { exact: true }).nth(0)).toBeVisible(); // check that 0h 00min is not displayed - await expect( - page.getByText('0h 00min', { exact: true }).nth(0) - ).not.toBeVisible(); + await expect(page.getByText('0h 00min', { exact: true }).nth(0)).not.toBeVisible(); // check that the current date is displayed in the dd/mm/yyyy format on the time page await page.goto(PLAYWRIGHT_BASE_URL + '/time'); await expect( - page - .getByText(new Date().toLocaleDateString('en-GB'), { exact: true }) - .nth(0) + page.getByText(new Date().toLocaleDateString('en-GB'), { exact: true }).nth(0) ).toBeVisible(); }); diff --git a/e2e/profile.spec.ts b/e2e/profile.spec.ts index 2428af9c..a2f34388 100644 --- a/e2e/profile.spec.ts +++ b/e2e/profile.spec.ts @@ -1,34 +1,32 @@ -import {test, expect} from '../playwright/fixtures'; -import {PLAYWRIGHT_BASE_URL} from '../playwright/config'; +import { test, expect } from '../playwright/fixtures'; +import { PLAYWRIGHT_BASE_URL } from '../playwright/config'; -test('test that user name can be updated', async ({page}) => { +test('test that user name can be updated', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); - await page.getByLabel('Name', {exact: true} ).fill('NEW NAME'); + await page.getByLabel('Name', { exact: true }).fill('NEW NAME'); await Promise.all([ - page.getByRole('button', {name: 'Save'}).first().click(), + page.getByRole('button', { name: 'Save' }).first().click(), page.waitForResponse('**/user/profile-information'), ]); await page.reload(); - await expect(page.getByLabel('Name', {exact: true})).toHaveValue('NEW NAME'); + await expect(page.getByLabel('Name', { exact: true })).toHaveValue('NEW NAME'); }); -test.skip('test that user email can be updated', async ({page}) => { +test.skip('test that user email can be updated', async ({ page }) => { // this does not work because of email verification currently await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); const emailId = Math.round(Math.random() * 10000); await page.getByLabel('Email').fill(`newemail+${emailId}@test.com`); - await page.getByRole('button', {name: 'Save'}).first().click(); + await page.getByRole('button', { name: 'Save' }).first().click(); await page.reload(); - await expect(page.getByLabel('Email')).toHaveValue( - `newemail+${emailId}@test.com` - ); + await expect(page.getByLabel('Email')).toHaveValue(`newemail+${emailId}@test.com`); }); async function createNewApiToken(page) { await page.getByLabel('API Key Name').fill('NEW API KEY'); await Promise.all([ - page.getByRole('button', {name: 'Create API Key'}).click(), - page.waitForResponse('**/users/me/api-tokens') + page.getByRole('button', { name: 'Create API Key' }).click(), + page.waitForResponse('**/users/me/api-tokens'), ]); await expect(page.locator('body')).toContainText('API Token created successfully'); @@ -36,34 +34,37 @@ async function createNewApiToken(page) { await expect(page.locator('body')).toContainText('NEW API KEY'); } -test('test that user can create an API key', async ({page}) => { +test('test that user can create an API key', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); await createNewApiToken(page); }); -test('test that user can delete an API key', async ({page}) => { +test('test that user can delete an API key', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); await createNewApiToken(page); page.getByLabel('Delete API Token NEW API KEY').click(); - await expect(page.getByRole('dialog')).toContainText('Are you sure you would like to delete this API token?'); + await expect(page.getByRole('dialog')).toContainText( + 'Are you sure you would like to delete this API token?' + ); await Promise.all([ - page.getByRole('dialog').getByRole('button', {name: 'Delete'}).click(), - page.waitForResponse('**/users/me/api-tokens') + page.getByRole('dialog').getByRole('button', { name: 'Delete' }).click(), + page.waitForResponse('**/users/me/api-tokens'), ]); await expect(page.locator('body')).not.toContainText('NEW API KEY'); }); - -test('test that user can revoke an API key', async ({page}) => { +test('test that user can revoke an API key', async ({ page }) => { await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile'); await createNewApiToken(page); page.getByLabel('Revoke API Token NEW API KEY').click(); - await expect(page.getByRole('dialog')).toContainText('Are you sure you would like to revoke this API token?'); + await expect(page.getByRole('dialog')).toContainText( + 'Are you sure you would like to revoke this API token?' + ); await Promise.all([ - page.getByRole('dialog').getByRole('button', {name: 'Revoke'}).click(), - page.waitForResponse('**/users/me/api-tokens') + page.getByRole('dialog').getByRole('button', { name: 'Revoke' }).click(), + page.waitForResponse('**/users/me/api-tokens'), ]); - await expect(page.getByRole('button', {name: 'Revoke'})).toBeHidden(); + await expect(page.getByRole('button', { name: 'Revoke' })).toBeHidden(); await expect(page.locator('body')).toContainText('NEW API KEY'); await expect(page.locator('body')).toContainText('Revoked'); }); diff --git a/e2e/project-members.spec.ts b/e2e/project-members.spec.ts index 66434eab..36ee7f2b 100644 --- a/e2e/project-members.spec.ts +++ b/e2e/project-members.spec.ts @@ -12,8 +12,7 @@ async function goToProjectsOverview(page: Page) { test('test that updating project member billable rate works for existing time entries', async ({ page, }) => { - const newProjectName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); const newBillableRate = Math.round(Math.random() * 10000); await goToProjectsOverview(page); await page.getByRole('button', { name: 'Create Project' }).click(); @@ -36,9 +35,7 @@ test('test that updating project member billable rate works for existing time en .first() .getByRole('button') .click(); - await page - .getByRole('menuitem', { name: 'Edit Project Member' }) - .click(); + await page.getByRole('menuitem', { name: 'Edit Project Member' }).click(); await page.getByLabel('Billable Rate').fill(newBillableRate.toString()); await page.getByRole('button', { name: 'Update Project Member' }).click(); @@ -55,8 +52,7 @@ test('test that updating project member billable rate works for existing time en response.url().includes('/project-members/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.billable_rate === - newBillableRate * 100 + (await response.json()).data.billable_rate === newBillableRate * 100 ), ]); await expect( diff --git a/e2e/projects.spec.ts b/e2e/projects.spec.ts index b751601a..9dd51c5d 100644 --- a/e2e/projects.spec.ts +++ b/e2e/projects.spec.ts @@ -9,11 +9,8 @@ async function goToProjectsOverview(page: Page) { } // Create new project via modal -test('test that creating and deleting a new project via the modal works', async ({ - page, -}) => { - const newProjectName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); +test('test that creating and deleting a new project via the modal works', async ({ page }) => { + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); await goToProjectsOverview(page); await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); @@ -31,16 +28,10 @@ test('test that creating and deleting a new project via the modal works', async ), ]); - await expect(page.getByTestId('project_table')).toContainText( - newProjectName - ); - const moreButton = page.locator( - "[aria-label='Actions for Project " + newProjectName + "']" - ); + await expect(page.getByTestId('project_table')).toContainText(newProjectName); + const moreButton = page.locator("[aria-label='Actions for Project " + newProjectName + "']"); moreButton.click(); - const deleteButton = page.locator( - "[aria-label='Delete Project " + newProjectName + "']" - ); + const deleteButton = page.locator("[aria-label='Delete Project " + newProjectName + "']"); await Promise.all([ deleteButton.click(), @@ -51,14 +42,11 @@ test('test that creating and deleting a new project via the modal works', async response.status() === 204 ), ]); - await expect(page.getByTestId('project_table')).not.toContainText( - newProjectName - ); + await expect(page.getByTestId('project_table')).not.toContainText(newProjectName); }); test('test that archiving and unarchiving projects works', async ({ page }) => { - const newProjectName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); await goToProjectsOverview(page); await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); @@ -87,11 +75,8 @@ test('test that archiving and unarchiving projects works', async ({ page }) => { ]); }); -test('test that updating billable rate works with existing time entries', async ({ - page, -}) => { - const newProjectName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); +test('test that updating billable rate works with existing time entries', async ({ page }) => { + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); const newBillableRate = Math.round(Math.random() * 10000); await goToProjectsOverview(page); await page.getByRole('button', { name: 'Create Project' }).click(); @@ -104,15 +89,11 @@ test('test that updating billable rate works with existing time entries', async await page.getByRole('menuitem').getByText('Edit').first().click(); await page.getByText('Non-Billable').click(); await page.getByText('Custom Rate').click(); - await page - .getByPlaceholder('Billable Rate') - .fill(newBillableRate.toString()); + await page.getByPlaceholder('Billable Rate').fill(newBillableRate.toString()); await page.getByRole('button', { name: 'Update Project' }).click(); await Promise.all([ - page - .locator('button').filter({ hasText: 'Yes, update existing time' }) - .click(), + page.locator('button').filter({ hasText: 'Yes, update existing time' }).click(), page.waitForRequest( async (request) => request.url().includes('/projects/') && @@ -124,8 +105,7 @@ test('test that updating billable rate works with existing time entries', async response.url().includes('/projects/') && response.request().method() === 'PUT' && response.status() === 200 && - (await response.json()).data.billable_rate === - newBillableRate * 100 + (await response.json()).data.billable_rate === newBillableRate * 100 ), ]); await expect( diff --git a/e2e/reporting.spec.ts b/e2e/reporting.spec.ts index 6b2f59c2..0cfa872c 100644 --- a/e2e/reporting.spec.ts +++ b/e2e/reporting.spec.ts @@ -2,8 +2,6 @@ import { expect, Page } from '@playwright/test'; import { PLAYWRIGHT_BASE_URL } from '../playwright/config'; import { test } from '../playwright/fixtures'; - - async function goToTimeOverview(page: Page) { await page.goto(PLAYWRIGHT_BASE_URL + '/time'); } @@ -31,7 +29,10 @@ async function createTimeEntryWithProject(page: Page, projectName: string, durat await page.getByRole('button', { name: 'Manual time entry' }).click(); // Fill in the time entry details - await page.getByRole('dialog').getByRole('textbox', { name: 'Description' }).fill(`Time entry for ${projectName}`); + await page + .getByRole('dialog') + .getByRole('textbox', { name: 'Description' }) + .fill(`Time entry for ${projectName}`); await page.getByRole('button', { name: 'No Project' }).click(); await page.getByText(projectName).click(); @@ -43,7 +44,9 @@ async function createTimeEntryWithProject(page: Page, projectName: string, durat // Submit the time entry await Promise.all([ page.getByRole('button', { name: 'Create Time Entry' }).click(), - page.waitForResponse(response => response.url().includes('/time-entries') && response.status() === 201) + page.waitForResponse( + (response) => response.url().includes('/time-entries') && response.status() === 201 + ), ]); } @@ -52,7 +55,10 @@ async function createTimeEntryWithTag(page: Page, tagName: string, duration: str await page.getByRole('button', { name: 'Manual time entry' }).click(); // Fill in the time entry details - await page.getByRole('dialog').getByRole('textbox', { name: 'Description' }).fill(`Time entry with tag ${tagName}`); + await page + .getByRole('dialog') + .getByRole('textbox', { name: 'Description' }) + .fill(`Time entry with tag ${tagName}`); // Add tag await page.getByRole('button', { name: 'Tags' }).click(); @@ -69,12 +75,19 @@ async function createTimeEntryWithTag(page: Page, tagName: string, duration: str await page.getByRole('button', { name: 'Create Time Entry' }).click(); } -async function createTimeEntryWithBillableStatus(page: Page, isBillable: boolean, duration: string) { +async function createTimeEntryWithBillableStatus( + page: Page, + isBillable: boolean, + duration: string +) { await goToTimeOverview(page); await page.getByRole('button', { name: 'Manual time entry' }).click(); // Fill in the time entry details - await page.getByRole('dialog').getByRole('textbox', { name: 'Description' }).fill(`Time entry ${isBillable ? 'billable' : 'non-billable'}`); + await page + .getByRole('dialog') + .getByRole('textbox', { name: 'Description' }) + .fill(`Time entry ${isBillable ? 'billable' : 'non-billable'}`); // Set billable status await page.getByRole('button', { name: 'Non-Billable' }).click(); @@ -109,7 +122,10 @@ test('test that project filtering works in reporting', async ({ page }) => { // escape page.keyboard.press('Escape'), // wait for API request to finish - page.waitForResponse(response => response.url().includes('/time-entries/aggregate') && response.status() === 200) + page.waitForResponse( + (response) => + response.url().includes('/time-entries/aggregate') && response.status() === 200 + ), ]); await page.waitForLoadState('networkidle'); @@ -138,7 +154,10 @@ test('test that tag filtering works in reporting', async ({ page }) => { // escape page.keyboard.press('Escape'), // wait for API request to finish - page.waitForResponse(response => response.url().includes('/time-entries/aggregate') && response.status() === 200) + page.waitForResponse( + (response) => + response.url().includes('/time-entries/aggregate') && response.status() === 200 + ), ]); // Verify only time entries with tag1 are shown @@ -160,14 +179,16 @@ test('test that billable status filtering works in reporting', async ({ page }) // escape page.keyboard.press('Escape'), // wait for API request to finish - page.waitForResponse(response => response.url().includes('/time-entries/aggregate') && response.status() === 200) + page.waitForResponse( + (response) => + response.url().includes('/time-entries/aggregate') && response.status() === 200 + ), ]); await page.waitForLoadState('networkidle'); await expect(page.getByTestId('reporting_view').getByText('1h 00min').first()).toBeVisible(); }); - test('test that detailed view shows time entries correctly', async ({ page }) => { const projectName = 'Detailed View Project ' + Math.floor(Math.random() * 10000); diff --git a/e2e/tags.spec.ts b/e2e/tags.spec.ts index 4a29c3db..8b89aa2b 100644 --- a/e2e/tags.spec.ts +++ b/e2e/tags.spec.ts @@ -7,9 +7,7 @@ async function goToTagsOverview(page: Page) { } // Create new project via modal -test('test that creating and deleting a new client via the modal works', async ({ - page, -}) => { +test('test that creating and deleting a new client via the modal works', async ({ page }) => { const newTagName = 'New Tag ' + Math.floor(1 + Math.random() * 10000); await goToTagsOverview(page); await page.getByRole('button', { name: 'Create Tag' }).click(); @@ -27,13 +25,9 @@ test('test that creating and deleting a new client via the modal works', async ( ]); await expect(page.getByTestId('tag_table')).toContainText(newTagName); - const moreButton = page.locator( - "[aria-label='Actions for Tag " + newTagName + "']" - ); + const moreButton = page.locator("[aria-label='Actions for Tag " + newTagName + "']"); moreButton.click(); - const deleteButton = page.locator( - "[aria-label='Delete Tag " + newTagName + "']" - ); + const deleteButton = page.locator("[aria-label='Delete Tag " + newTagName + "']"); await Promise.all([ deleteButton.click(), diff --git a/e2e/tasks.spec.ts b/e2e/tasks.spec.ts index d5165993..8e2073db 100644 --- a/e2e/tasks.spec.ts +++ b/e2e/tasks.spec.ts @@ -7,11 +7,8 @@ async function goToProjectsOverview(page: Page) { } // Create new project via modal -test('test that creating and deleting a new tag in a new project works', async ({ - page, -}) => { - const newProjectName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); +test('test that creating and deleting a new tag in a new project works', async ({ page }) => { + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); await goToProjectsOverview(page); await page.getByRole('button', { name: 'Create Project' }).click(); await page.getByLabel('Project Name').fill(newProjectName); @@ -29,9 +26,7 @@ test('test that creating and deleting a new tag in a new project works', async ( ), ]); - await expect(page.getByTestId('project_table')).toContainText( - newProjectName - ); + await expect(page.getByTestId('project_table')).toContainText(newProjectName); await page.getByText(newProjectName).click(); @@ -55,13 +50,9 @@ test('test that creating and deleting a new tag in a new project works', async ( await expect(page.getByTestId('task_table')).toContainText(newTaskName); - const taskMoreButton = page.locator( - "[aria-label='Actions for Task " + newTaskName + "']" - ); + const taskMoreButton = page.locator("[aria-label='Actions for Task " + newTaskName + "']"); taskMoreButton.click(); - const taskDeleteButton = page.locator( - "[aria-label='Delete Task " + newTaskName + "']" - ); + const taskDeleteButton = page.locator("[aria-label='Delete Task " + newTaskName + "']"); await Promise.all([ taskDeleteButton.click(), @@ -76,13 +67,9 @@ test('test that creating and deleting a new tag in a new project works', async ( await goToProjectsOverview(page); - const moreButton = page.locator( - "[aria-label='Actions for Project " + newProjectName + "']" - ); + const moreButton = page.locator("[aria-label='Actions for Project " + newProjectName + "']"); moreButton.click(); - const deleteButton = page.locator( - "[aria-label='Delete Project " + newProjectName + "']" - ); + const deleteButton = page.locator("[aria-label='Delete Project " + newProjectName + "']"); await Promise.all([ deleteButton.click(), @@ -93,14 +80,11 @@ test('test that creating and deleting a new tag in a new project works', async ( response.status() === 204 ), ]); - await expect(page.getByTestId('project_table')).not.toContainText( - newProjectName - ); + await expect(page.getByTestId('project_table')).not.toContainText(newProjectName); }); test('test that archiving and unarchiving tasks works', async ({ page }) => { - const newProjectName = - 'New Project ' + Math.floor(1 + Math.random() * 10000); + const newProjectName = 'New Project ' + Math.floor(1 + Math.random() * 10000); const newTaskName = 'New Project ' + Math.floor(1 + Math.random() * 10000); await goToProjectsOverview(page); diff --git a/e2e/time.spec.ts b/e2e/time.spec.ts index da2aa7ce..2b01ddfb 100644 --- a/e2e/time.spec.ts +++ b/e2e/time.spec.ts @@ -25,9 +25,7 @@ async function createEmptyTimeEntry(page: Page) { startOrStopTimerWithButton(page), assertThatTimerIsStopped(page), page.waitForResponse( - (response) => - response.url().includes('/time-entries') && - response.status() === 200 + (response) => response.url().includes('/time-entries') && response.status() === 200 ), ]); } @@ -38,9 +36,7 @@ test('test that starting and stopping an empty time entry shows a new time entry await Promise.all([ goToTimeOverview(page), page.waitForResponse( - (response) => - response.url().includes('/time-entries') && - response.status() === 200 + (response) => response.url().includes('/time-entries') && response.status() === 200 ), ]); await page.waitForTimeout(100); @@ -56,9 +52,7 @@ test('test that starting and stopping an empty time entry shows a new time entry // Test that description update works async function assertThatTimeEntryRowIsStopped(newTimeEntry: Locator) { - await expect(newTimeEntry.getByTestId('timer_button')).toHaveClass( - /bg-accent-300\/70/ - ); + await expect(newTimeEntry.getByTestId('timer_button')).toHaveClass(/bg-accent-300\/70/); } test('test that updating a description of a time entry in the overview works on blur', async ({ @@ -71,17 +65,14 @@ test('test that updating a description of a time entry in the overview works on await assertThatTimeEntryRowIsStopped(newTimeEntry); const newDescription = Math.floor(Math.random() * 1000000).toString(); - const descriptionElement = newTimeEntry.getByTestId( - 'time_entry_description' - ); + const descriptionElement = newTimeEntry.getByTestId('time_entry_description'); await descriptionElement.fill(newDescription); await Promise.all([ descriptionElement.press('Tab'), page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end !== null && @@ -90,8 +81,7 @@ test('test that updating a description of a time entry in the overview works on (await response.json()).data.task_id === null && (await response.json()).data.duration !== null && (await response.json()).data.user_id !== null && - JSON.stringify((await response.json()).data.tags) === - JSON.stringify([]) + JSON.stringify((await response.json()).data.tags) === JSON.stringify([]) ); }), ]); @@ -107,17 +97,14 @@ test('test that updating a description of a time entry in the overview works on const newTimeEntry = timeEntryRows.first(); await assertThatTimeEntryRowIsStopped(newTimeEntry); const newDescription = Math.floor(Math.random() * 1000000).toString(); - const descriptionElement = newTimeEntry.getByTestId( - 'time_entry_description' - ); + const descriptionElement = newTimeEntry.getByTestId('time_entry_description'); await descriptionElement.fill(newDescription); await Promise.all([ descriptionElement.press('Enter'), page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end !== null && @@ -126,16 +113,13 @@ test('test that updating a description of a time entry in the overview works on (await response.json()).data.task_id === null && (await response.json()).data.duration !== null && (await response.json()).data.user_id !== null && - JSON.stringify((await response.json()).data.tags) === - JSON.stringify([]) + JSON.stringify((await response.json()).data.tags) === JSON.stringify([]) ); }), ]); }); -test('test that adding a new tag to an existing time entry works', async ({ - page, -}) => { +test('test that adding a new tag to an existing time entry works', async ({ page }) => { await goToTimeOverview(page); const timeEntryRows = page.locator('[data-testid="time_entry_row"]'); await createEmptyTimeEntry(page); @@ -152,8 +136,7 @@ test('test that adding a new tag to an existing time entry works', async ({ page.waitForResponse(async (response) => { return ( response.status() === 201 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.name === newTagName ); }), @@ -163,8 +146,7 @@ test('test that adding a new tag to an existing time entry works', async ({ await page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end !== null && @@ -187,17 +169,14 @@ test('test that updating a the start of an existing time entry in the overview w const newTimeEntry = timeEntryRows.first(); await assertThatTimeEntryRowIsStopped(newTimeEntry); await page.waitForTimeout(1500); - const timeEntryRangeElement = newTimeEntry.getByTestId( - 'time_entry_range_selector' - ); + const timeEntryRangeElement = newTimeEntry.getByTestId('time_entry_range_selector'); await timeEntryRangeElement.click(); await page.getByTestId('time_entry_range_start').first().fill('1'); await Promise.all([ page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && // TODO! Actually check the value (await response.json()).data.start !== null && @@ -208,9 +187,7 @@ test('test that updating a the start of an existing time entry in the overview w ]); }); -test('test that updating a the duration in the overview works on blur', async ({ - page, -}) => { +test('test that updating a the duration in the overview works on blur', async ({ page }) => { await goToTimeOverview(page); const timeEntryRows = page.locator('[data-testid="time_entry_row"]'); await createEmptyTimeEntry(page); @@ -225,8 +202,7 @@ test('test that updating a the duration in the overview works on blur', async ({ page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && // TODO! Actually check the value (await response.json()).data.start !== null && @@ -240,9 +216,7 @@ test('test that updating a the duration in the overview works on blur', async ({ }); // Test that start stop button stops running timer -test('test that starting a time entry from the overview works', async ({ - page, -}) => { +test('test that starting a time entry from the overview works', async ({ page }) => { await goToTimeOverview(page); const timeEntryRows = page.locator('[data-testid="time_entry_row"]'); await createEmptyTimeEntry(page); @@ -255,8 +229,7 @@ test('test that starting a time entry from the overview works', async ({ page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end !== null @@ -272,8 +245,7 @@ test('test that starting a time entry from the overview works', async ({ page.waitForResponse(async (response) => { return ( response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end !== null @@ -284,9 +256,7 @@ test('test that starting a time entry from the overview works', async ({ ]); }); -test('test that deleting a time entry from the overview works', async ({ - page, -}) => { +test('test that deleting a time entry from the overview works', async ({ page }) => { await goToTimeOverview(page); const timeEntryRows = page.locator('[data-testid="time_entry_row"]'); await createEmptyTimeEntry(page); @@ -302,16 +272,12 @@ test('test that deleting a time entry from the overview works', async ({ await expect(timeEntryRows).toHaveCount(0); }); -test.skip('test that load more works when the end of page is reached', async ({ - page, -}) => { +test.skip('test that load more works when the end of page is reached', async ({ page }) => { // this test is flaky when you do not need to scroll await Promise.all([ goToTimeOverview(page), page.waitForResponse( - (response) => - response.url().includes('/time-entries') && - response.status() === 200 + (response) => response.url().includes('/time-entries') && response.status() === 200 ), ]); @@ -322,18 +288,14 @@ test.skip('test that load more works when the end of page is reached', async ({ return ( response.status() === 200 && response.url().includes('before') && - (await response.headerValue('Content-Type')) === - 'application/json' && - JSON.stringify((await response.json()).data) === - JSON.stringify([]) + (await response.headerValue('Content-Type')) === 'application/json' && + JSON.stringify((await response.json()).data) === JSON.stringify([]) ); }), ]); // assert that "All time entries are loaded!" is visible on page - await expect(page.locator('body')).toHaveText( - /All time entries are loaded!/ - ); + await expect(page.locator('body')).toHaveText(/All time entries are loaded!/); }); // TODO: Test that updating the time entry start / end times works while it is running diff --git a/e2e/timetracker.spec.ts b/e2e/timetracker.spec.ts index df4a210f..c7896ca1 100644 --- a/e2e/timetracker.spec.ts +++ b/e2e/timetracker.spec.ts @@ -24,22 +24,15 @@ test('test that starting and stopping a timer without description and project wo assertThatTimerHasStarted(page), ]); await page.waitForTimeout(1500); - await Promise.all([ - stoppedTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([stoppedTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await assertThatTimerIsStopped(page); }); -test('test that starting and stopping a timer with a description works', async ({ - page, -}) => { +test('test that starting and stopping a timer with a description works', async ({ page }) => { await goToDashboard(page); // TODO: Fix flakyness by disabling description input field until timer is loaded await page.waitForTimeout(500); - await page - .getByTestId('time_entry_description') - .fill('New Time Entry Description'); + await page.getByTestId('time_entry_description').fill('New Time Entry Description'); await Promise.all([ newTimeEntryResponse(page, { description: 'New Time Entry Description', @@ -62,47 +55,29 @@ test('test that starting the time entry starts the live timer and that it keeps }) => { await goToDashboard(page); - await Promise.all([ - newTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([newTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await assertThatTimerHasStarted(page); await page.waitForTimeout(500); - const beforeTimerValue = await page - .getByTestId('time_entry_time') - .inputValue(); + const beforeTimerValue = await page.getByTestId('time_entry_time').inputValue(); await page.waitForTimeout(2000); - const afterWaitTimeValue = await page - .getByTestId('time_entry_time') - .inputValue(); + const afterWaitTimeValue = await page.getByTestId('time_entry_time').inputValue(); expect(afterWaitTimeValue).not.toEqual(beforeTimerValue); await page.reload(); await page.waitForTimeout(500); - const afterReloadTimerValue = await page - .getByTestId('time_entry_time') - .inputValue(); + const afterReloadTimerValue = await page.getByTestId('time_entry_time').inputValue(); await page.waitForTimeout(2000); - const afterReloadAfterWaitTimerValue = await page - .getByTestId('time_entry_time') - .inputValue(); + const afterReloadAfterWaitTimerValue = await page.getByTestId('time_entry_time').inputValue(); expect(afterReloadTimerValue).not.toEqual(afterReloadAfterWaitTimerValue); }); -test('test that starting and updating the description while running works', async ({ - page, -}) => { +test('test that starting and updating the description while running works', async ({ page }) => { await goToDashboard(page); - await Promise.all([ - newTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([newTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await assertThatTimerHasStarted(page); await page.waitForTimeout(500); - await page - .getByTestId('time_entry_description') - .fill('New Time Entry Description'); + await page.getByTestId('time_entry_description').fill('New Time Entry Description'); await Promise.all([ newTimeEntryResponse(page, { @@ -121,9 +96,7 @@ test('test that starting and updating the description while running works', asyn await assertThatTimerIsStopped(page); }); -test('test that starting and updating the time while running works', async ({ - page, -}) => { +test('test that starting and updating the time while running works', async ({ page }) => { await goToDashboard(page); const [createResponse] = await Promise.all([ newTimeEntryResponse(page), @@ -138,19 +111,16 @@ test('test that starting and updating the time while running works', async ({ return ( response.url().includes('/time-entries') && response.status() === 200 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && - (await response.json()).data.start !== - (await createResponse.json()).data.start && + (await response.json()).data.start !== (await createResponse.json()).data.start && (await response.json()).data.end === null && (await response.json()).data.project_id === null && (await response.json()).data.description === '' && (await response.json()).data.task_id === null && (await response.json()).data.user_id !== null && - JSON.stringify((await response.json()).data.tags) === - JSON.stringify([]) + JSON.stringify((await response.json()).data.tags) === JSON.stringify([]) ); }), page.getByTestId('time_entry_time').press('Enter'), @@ -158,16 +128,11 @@ test('test that starting and updating the time while running works', async ({ await expect(page.getByTestId('time_entry_time')).toHaveValue(/00:20/); await page.waitForTimeout(500); - await Promise.all([ - stoppedTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([stoppedTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await assertThatTimerIsStopped(page); }); -test('test that entering a human readable time starts the timer on blur', async ({ - page, -}) => { +test('test that entering a human readable time starts the timer on blur', async ({ page }) => { await goToDashboard(page); await page.getByTestId('time_entry_time').fill('20min'); await Promise.all([ @@ -177,18 +142,13 @@ test('test that entering a human readable time starts the timer on blur', async await expect(page.getByTestId('time_entry_time')).toHaveValue(/00:20:/); await assertThatTimerHasStarted(page); - await Promise.all([ - stoppedTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([stoppedTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await page.locator( '[data-testid="dashboard_timer"] [data-testid="timer_button"].bg-accent-300/70' ); }); -test('test that entering a number in the time range starts the timer on blur', async ({ - page, -}) => { +test('test that entering a number in the time range starts the timer on blur', async ({ page }) => { await goToDashboard(page); await page.getByTestId('time_entry_time').fill('5'); await Promise.all([ @@ -198,10 +158,7 @@ test('test that entering a number in the time range starts the timer on blur', a await expect(page.getByTestId('time_entry_time')).toHaveValue(/00:05:/); await assertThatTimerHasStarted(page); - await Promise.all([ - stoppedTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([stoppedTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await page.locator( '[data-testid="dashboard_timer"] [data-testid="timer_button"].bg-accent-300/70' ); @@ -219,10 +176,7 @@ test('test that entering a value with the format hh:mm in the time range starts await expect(page.getByTestId('time_entry_time')).toHaveValue(/12:30:/); await assertThatTimerHasStarted(page); - await Promise.all([ - stoppedTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([stoppedTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await page.locator( '[data-testid="dashboard_timer"] [data-testid="timer_button"].bg-accent-300/70' ); @@ -239,9 +193,7 @@ test('test that entering a random value in the time range does not start the tim ); }); -test('test that entering a time starts the timer on enter', async ({ - page, -}) => { +test('test that entering a time starts the timer on enter', async ({ page }) => { await goToDashboard(page); await page.getByTestId('time_entry_time').fill('20min'); await Promise.all([ @@ -249,10 +201,7 @@ test('test that entering a time starts the timer on enter', async ({ page.getByTestId('time_entry_time').press('Enter'), ]); await assertThatTimerHasStarted(page); - await Promise.all([ - stoppedTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([stoppedTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await assertThatTimerIsStopped(page); }); @@ -273,15 +222,10 @@ test('test that adding a new tag works', async ({ page }) => { await expect(page.getByRole('option', { name: newTagName })).toBeVisible(); }); -test('test that adding a new tag when the timer is running', async ({ - page, -}) => { +test('test that adding a new tag when the timer is running', async ({ page }) => { const newTagName = 'New Tag' + Math.floor(Math.random() * 10000); await goToDashboard(page); - await Promise.all([ - newTimeEntryResponse(page), - startOrStopTimerWithButton(page), - ]); + await Promise.all([newTimeEntryResponse(page), startOrStopTimerWithButton(page)]); await assertThatTimerHasStarted(page); await page.getByTestId('tag_dropdown').click(); await page.getByText('Create new tag').click(); diff --git a/e2e/utils/currentTimeEntry.ts b/e2e/utils/currentTimeEntry.ts index 720fddde..162975ec 100644 --- a/e2e/utils/currentTimeEntry.ts +++ b/e2e/utils/currentTimeEntry.ts @@ -1,9 +1,7 @@ import { expect, Page } from '@playwright/test'; export async function startOrStopTimerWithButton(page: Page) { - await page - .locator('[data-testid="dashboard_timer"] [data-testid="timer_button"]') - .click(); + await page.locator('[data-testid="dashboard_timer"] [data-testid="timer_button"]').click(); } export async function assertThatTimerHasStarted(page: Page) { @@ -20,8 +18,7 @@ export function newTimeEntryResponse( return ( response.url().includes('/time-entries') && response.status() === status && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end === null && @@ -29,30 +26,23 @@ export function newTimeEntryResponse( (await response.json()).data.description === description && (await response.json()).data.task_id === null && (await response.json()).data.user_id !== null && - JSON.stringify((await response.json()).data.tags) === - JSON.stringify(tags) + JSON.stringify((await response.json()).data.tags) === JSON.stringify(tags) ); }); } export async function assertThatTimerIsStopped(page: Page) { await expect( - page.locator( - '[data-testid="dashboard_timer"] [data-testid="timer_button"]' - ) + page.locator('[data-testid="dashboard_timer"] [data-testid="timer_button"]') ).toHaveClass(/bg-accent-300\/70/); } -export async function stoppedTimeEntryResponse( - page: Page, - { description = '', tags = [] } = {} -) { +export async function stoppedTimeEntryResponse(page: Page, { description = '', tags = [] } = {}) { return page.waitForResponse(async (response) => { return ( response.status() === 200 && response.url().includes('/time-entries/') && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.id !== null && (await response.json()).data.start !== null && (await response.json()).data.end !== null && @@ -61,8 +51,7 @@ export async function stoppedTimeEntryResponse( (await response.json()).data.task_id === null && (await response.json()).data.duration !== null && (await response.json()).data.user_id !== null && - JSON.stringify((await response.json()).data.tags) === - JSON.stringify(tags) + JSON.stringify((await response.json()).data.tags) === JSON.stringify(tags) ); }); } diff --git a/e2e/utils/money.ts b/e2e/utils/money.ts index 34bf83dd..aed8176c 100644 --- a/e2e/utils/money.ts +++ b/e2e/utils/money.ts @@ -14,4 +14,4 @@ export function formatCentsWithOrganizationDefaults( currencySymbol, 'point-comma' as NumberFormat ); -} \ No newline at end of file +} diff --git a/e2e/utils/tags.ts b/e2e/utils/tags.ts index a0a3a701..888d6794 100644 --- a/e2e/utils/tags.ts +++ b/e2e/utils/tags.ts @@ -4,8 +4,7 @@ export function newTagResponse(page: Page, { name = '' } = {}) { return page.waitForResponse(async (response) => { return ( response.status() === 201 && - (await response.headerValue('Content-Type')) === - 'application/json' && + (await response.headerValue('Content-Type')) === 'application/json' && (await response.json()).data.name === name ); }); diff --git a/eslint.config.mjs b/eslint.config.mjs index 9b1c982f..2d6a685a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -3,7 +3,7 @@ import eslintConfigPrettier from 'eslint-config-prettier'; import eslintPluginVue from 'eslint-plugin-vue'; import globals from 'globals'; import typescriptEslint from 'typescript-eslint'; -import unusedImports from "eslint-plugin-unused-imports"; +import unusedImports from 'eslint-plugin-unused-imports'; export default typescriptEslint.config( { ignores: ['*.d.ts', '**/coverage', '**/dist'] }, @@ -23,18 +23,21 @@ export default typescriptEslint.config( }, }, plugins: { - "unused-imports": unusedImports, + 'unused-imports': unusedImports, }, rules: { - "vue/multi-word-component-names": "off", - "@typescript-eslint/no-unused-vars": "off", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": ["error", { - "vars": "all", - "varsIgnorePattern": "^_", - "args": "after-used", - "argsIgnorePattern": "^_", - }], + 'vue/multi-word-component-names': 'off', + '@typescript-eslint/no-unused-vars': 'off', + 'unused-imports/no-unused-imports': 'error', + 'unused-imports/no-unused-vars': [ + 'error', + { + 'vars': 'all', + 'varsIgnorePattern': '^_', + 'args': 'after-used', + 'argsIgnorePattern': '^_', + }, + ], }, }, eslintConfigPrettier diff --git a/package.json b/package.json index 159f4fda..c7c2a120 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "lint:fix": "eslint --fix resources/js", "type-check": "vue-tsc --noEmit", "test:e2e": "rm -rf test-results/.auth && npx playwright test", - "zod:generate": "npx openapi-zod-client http://localhost:80/docs/api.json --output resources/js/packages/api/src/openapi.json.client.ts --base-url /api" + "zod:generate": "npx openapi-zod-client http://localhost:80/docs/api.json --output resources/js/packages/api/src/openapi.json.client.ts --base-url /api", + "format": "prettier --write './**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,vue}'", + "format:check": "prettier --check './**/*.{js,jsx,cjs,mjs,ts,tsx,cts,mts,vue}'" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/playwright/config.ts b/playwright/config.ts index 06ada1f8..3d12852a 100644 --- a/playwright/config.ts +++ b/playwright/config.ts @@ -1,2 +1 @@ -export const PLAYWRIGHT_BASE_URL = - process.env.PLAYWRIGHT_BASE_URL ?? 'http://solidtime.test'; +export const PLAYWRIGHT_BASE_URL = process.env.PLAYWRIGHT_BASE_URL ?? 'http://solidtime.test'; diff --git a/playwright/fixtures.ts b/playwright/fixtures.ts index 80fadd92..93469ad3 100644 --- a/playwright/fixtures.ts +++ b/playwright/fixtures.ts @@ -8,12 +8,8 @@ export const test = baseTest.extend({ // Perform authentication steps. Replace these actions with your own. await page.goto(PLAYWRIGHT_BASE_URL + '/register'); await page.getByLabel('Name').fill('John Doe'); - await page - .getByLabel('Email') - .fill(`john+${Math.round(Math.random() * 1000000)}@doe.com`); - await page - .getByLabel('Password', { exact: true }) - .fill('amazingpassword123'); + await page.getByLabel('Email').fill(`john+${Math.round(Math.random() * 1000000)}@doe.com`); + await page.getByLabel('Password', { exact: true }).fill('amazingpassword123'); await page.getByLabel('Confirm Password').fill('amazingpassword123'); await page.getByLabel('I agree to the Terms of').click(); await page.getByRole('button', { name: 'Register' }).click(); diff --git a/resources/js/Components/ActionSection.vue b/resources/js/Components/ActionSection.vue index 6d07781e..6748475c 100644 --- a/resources/js/Components/ActionSection.vue +++ b/resources/js/Components/ActionSection.vue @@ -14,8 +14,7 @@ import SectionTitle from './SectionTitle.vue';
-
+
diff --git a/resources/js/Components/AuthenticationCard.vue b/resources/js/Components/AuthenticationCard.vue index 30ffea9b..4a8c6c17 100644 --- a/resources/js/Components/AuthenticationCard.vue +++ b/resources/js/Components/AuthenticationCard.vue @@ -1,9 +1,9 @@ diff --git a/resources/js/Components/Banner.vue b/resources/js/Components/Banner.vue index 4c69e0ff..475f0eae 100644 --- a/resources/js/Components/Banner.vue +++ b/resources/js/Components/Banner.vue @@ -24,9 +24,7 @@ watchEffect(async () => {