mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 00:02:15 +01:00
add format check, update prettier rules, apply rules consistently
This commit is contained in:
@@ -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 }) => {
|
||||
|
||||
@@ -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
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@ export function formatCentsWithOrganizationDefaults(
|
||||
currencySymbol,
|
||||
'point-comma' as NumberFormat
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user