Skip to content

Commit 192b33e

Browse files
authored
Fix Unsorted Training Log Display (#15)
* fix(api): sort training logs by date ascending * fix: input data type for animal card * feat(ui): implement reusable animals grid component * feat(api): fix hardcoded profile pic * feat(ui): make forms overflow on small layouts * fix(crud): remove confusing log for find user by email * feat(ui): make form overflow on smaller viewports * feat(ci): add token to env secrets * chore: add dotenv to deps * ci: add e2e tests to verify functionality * feat(ui): add notification for successful animal creation * add logging statement to debug connection failure * add debug stmt * add debug stmts * Remove later steps to debug missing env * feat(ci): add env to pipeline * tests: add env to pipeline * remove log stmt * tests: fix bug in test setup step
1 parent 17675c0 commit 192b33e

33 files changed

Lines changed: 618 additions & 497 deletions

‎.github/workflows/playwright.yml‎

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,35 @@
11
name: Playwright Tests
22
env:
3-
NEXTAUTH_URL: http://localhost:3000
4-
DB_NAME: 'animal-tracking'
5-
MONGO_DB: ${{ secrets.MONGO_DB }}
3+
NEXTAUTH_URL: http://localhost:3000
4+
DB_NAME: 'animal-tracking'
5+
MONGO_DB: ${{ secrets.MONGO_DB }}
6+
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
67
on:
7-
push:
8-
branches: [ main, master ]
9-
pull_request:
10-
branches: [ main, master ]
8+
push:
9+
branches: [main, master]
10+
pull_request:
11+
branches: [main, master]
1112
jobs:
12-
test:
13-
timeout-minutes: 60
14-
runs-on: ubuntu-latest
15-
steps:
16-
- uses: actions/checkout@v4
17-
- uses: actions/setup-node@v4
18-
with:
19-
node-version: lts/*
20-
- name: Install dependencies
21-
run: npm ci
22-
- name: Install Playwright Browsers
23-
run: npx playwright install --with-deps
24-
- name: Run Playwright tests
25-
run: npx playwright test
26-
- uses: actions/upload-artifact@v4
27-
if: ${{ !cancelled() }}
28-
with:
29-
name: playwright-report
30-
path: playwright-report/
31-
retention-days: 30
13+
test:
14+
environment: dev
15+
timeout-minutes: 60
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Print Environment Variables
19+
run: printenv
20+
- uses: actions/checkout@v4
21+
- uses: actions/setup-node@v4
22+
with:
23+
node-version: lts/*
24+
- name: Install dependencies
25+
run: npm ci
26+
- name: Install Playwright Browsers
27+
run: npx playwright install --with-deps
28+
- name: Run Playwright tests
29+
run: npx playwright test
30+
- uses: actions/upload-artifact@v4
31+
if: ${{ !cancelled() }}
32+
with:
33+
name: playwright-report
34+
path: playwright-report/
35+
retention-days: 30

‎e2e/animals.spec.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import test, { expect } from '@playwright/test';
2+
3+
test('create a new animal', async ({ page }) => {
4+
await page.goto('/');
5+
// Expect a title "to contain" a substring.
6+
await page.getByRole('link', { name: 'app logo Progress' }).click();
7+
await page.getByRole('link', { name: 'Log In' }).click();
8+
await page.getByRole('textbox', { name: 'Email' }).click();
9+
await page
10+
.getByRole('textbox', { name: 'Email' })
11+
.fill('alice3@gatech.edu');
12+
await page.getByRole('textbox', { name: 'Email' }).press('Tab');
13+
await page.getByRole('textbox', { name: 'Password' }).fill('12345678');
14+
await page.getByRole('button', { name: 'Log In' }).click();
15+
16+
await page.getByRole('link', { name: 'Animals icon Animals' }).click();
17+
await page
18+
.getByRole('link', { name: 'create new animal Create New' })
19+
.click();
20+
await page.getByRole('textbox', { name: 'Animal Name' }).click();
21+
await page.getByRole('textbox', { name: 'Animal Name' }).fill('Rover');
22+
await page.getByLabel('Breed').selectOption('German Shepherd');
23+
await page
24+
.getByRole('spinbutton', { name: 'Total hours trained' })
25+
.fill('5');
26+
27+
await page.getByRole('button', { name: 'Save' }).click();
28+
await expect(page.getByText('Animal created successfully!')).toBeVisible();
29+
30+
await page.getByRole('link', { name: 'Animals icon Animals' }).click();
31+
await expect(page.getByText('Rover - German Shepherd')).toBeVisible();
32+
});

‎e2e/auth.spec.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test.beforeAll(async ({ request }) => {
4+
const response = await request.post('/api/user/register', {
5+
data: {
6+
fullName: 'Admin User',
7+
email: 'admin@gatech.edu',
8+
password: '12345678',
9+
confirmPassword: '12345678',
10+
admin: true,
11+
},
12+
});
13+
await expect(response).toBeOK();
14+
});
15+
16+
test('create admin user', async ({ page }) => {
17+
await page.goto('/signup');
18+
// Expect a title "to contain" a substring.
19+
await page.getByRole('textbox', { name: 'Full Name' }).click();
20+
await page.getByRole('textbox', { name: 'Full Name' }).fill('Admin User 2');
21+
await page.getByRole('textbox', { name: 'Full Name' }).press('Tab');
22+
await page
23+
.getByRole('textbox', { name: 'Email' })
24+
.fill('admin2@gatech.edu');
25+
await page.getByRole('textbox', { name: 'Password', exact: true }).click();
26+
await page
27+
.getByRole('textbox', { name: 'Password', exact: true })
28+
.fill('12345678');
29+
await page.getByRole('textbox', { name: 'Confirm Password' }).click();
30+
await page
31+
.getByRole('textbox', { name: 'Confirm Password' })
32+
.fill('12345678');
33+
await page.getByRole('checkbox', { name: 'Admin Access' }).check();
34+
await page.getByRole('button', { name: 'Sign up' }).click();
35+
await expect(
36+
page.getByRole('heading', { name: 'Create Account' })
37+
).toBeVisible();
38+
});
39+
40+
test('login as admin user', async ({ page }) => {
41+
await page.goto('/');
42+
// Expect a title "to contain" a substring.
43+
await page.getByRole('link', { name: 'app logo Progress' }).click();
44+
await page.getByRole('link', { name: 'Log In' }).click();
45+
await page.getByRole('textbox', { name: 'Email' }).click();
46+
await page.getByRole('textbox', { name: 'Email' }).fill('admin@gatech.edu');
47+
await page.getByRole('textbox', { name: 'Email' }).press('Tab');
48+
await page.getByRole('textbox', { name: 'Password' }).fill('12345678');
49+
await page.getByRole('button', { name: 'Log In' }).click();
50+
await expect(
51+
page.getByRole('heading', { name: 'Admin Access' })
52+
).toBeVisible();
53+
});

‎e2e/example.spec.ts‎

Lines changed: 0 additions & 8 deletions
This file was deleted.

‎e2e/global-setup.ts‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { chromium, expect, request, type FullConfig } from '@playwright/test';
2+
import connectDB from '../server/mongodb';
3+
import { User } from '../server/mongodb/models/User';
4+
import { TrainingLog } from '../server/mongodb/models/TrainingLog';
5+
import { Animal } from '../server/mongodb/models/Animal';
6+
7+
async function globalSetup(config: FullConfig) {
8+
// clear the database and seed with users
9+
try {
10+
await connectDB();
11+
await User.collection.drop();
12+
await TrainingLog.collection.drop();
13+
await Animal.collection.drop();
14+
} catch (error) {
15+
console.error('Unable to reset the database');
16+
}
17+
console.log('URL ', `${config.webServer?.url}/api/user/register`);
18+
const requestCtx = await request.newContext();
19+
const response = await requestCtx.post(
20+
`${config.webServer?.url}/api/user/register`,
21+
{
22+
data: {
23+
fullName: 'Bob Johnson',
24+
email: 'alice3@gatech.edu',
25+
password: '12345678',
26+
confirmPassword: '12345678',
27+
admin: true,
28+
},
29+
}
30+
);
31+
32+
console.log('Response ', response);
33+
}
34+
35+
export default globalSetup;

‎e2e/train-logs.spec.ts‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import test, { expect } from '@playwright/test';
2+
test.describe.serial('training log tests ', () => {
3+
test.beforeAll(async ({ browser }) => {
4+
const page = await browser.newPage();
5+
await page.goto('/');
6+
// Expect a title "to contain" a substring.
7+
await page.getByRole('link', { name: 'app logo Progress' }).click();
8+
await page.getByRole('link', { name: 'Log In' }).click();
9+
await page.getByRole('textbox', { name: 'Email' }).click();
10+
await page
11+
.getByRole('textbox', { name: 'Email' })
12+
.fill('alice3@gatech.edu');
13+
await page.getByRole('textbox', { name: 'Email' }).press('Tab');
14+
await page.getByRole('textbox', { name: 'Password' }).fill('12345678');
15+
await page.getByRole('button', { name: 'Log In' }).click();
16+
await page.getByRole('link', { name: 'Animals icon Animals' }).click();
17+
await page
18+
.getByRole('link', { name: 'create new animal Create New' })
19+
.click();
20+
await page.getByRole('textbox', { name: 'Animal Name' }).click();
21+
await page.getByRole('textbox', { name: 'Animal Name' }).fill('Rover');
22+
await page.getByLabel('Breed').selectOption('German Shepherd');
23+
await page
24+
.getByRole('spinbutton', { name: 'Total hours trained' })
25+
.fill('5');
26+
27+
await page.getByRole('button', { name: 'Save' }).click();
28+
await expect(
29+
page.getByText('Animal created successfully!')
30+
).toBeVisible();
31+
});
32+
33+
test('create a new training log', async ({ page }) => {
34+
await page.goto('/');
35+
// Expect a title "to contain" a substring.
36+
await page.getByRole('link', { name: 'app logo Progress' }).click();
37+
await page.getByRole('link', { name: 'Log In' }).click();
38+
await page.getByRole('textbox', { name: 'Email' }).click();
39+
await page
40+
.getByRole('textbox', { name: 'Email' })
41+
.fill('alice3@gatech.edu');
42+
await page.getByRole('textbox', { name: 'Email' }).press('Tab');
43+
await page.getByRole('textbox', { name: 'Password' }).fill('12345678');
44+
await page.getByRole('button', { name: 'Log In' }).click();
45+
await page
46+
.getByRole('link', { name: 'create new training log' })
47+
.click();
48+
await page.getByRole('textbox', { name: 'Title' }).click();
49+
await page
50+
.getByRole('textbox', { name: 'Title' })
51+
.fill('Fetch Practice');
52+
53+
await page.getByLabel('Select Animal').selectOption({ label: 'Rover' });
54+
await page.getByRole('textbox', { name: 'Description' }).click();
55+
await page
56+
.getByRole('textbox', { name: 'Description' })
57+
.fill('Rover played fetch and returned the ball every time.');
58+
await page.getByRole('button', { name: 'Save' }).click();
59+
await expect(page.getByText('Training log created')).toBeVisible();
60+
61+
await page
62+
.getByRole('link', { name: 'Training Logs icon Training' })
63+
.click();
64+
await expect(page.getByRole('main')).toContainText(
65+
'Rover played fetch and returned the ball every time.'
66+
);
67+
});
68+
69+
test('edit a training log', async ({ page }) => {
70+
await page.goto('/');
71+
// Expect a title "to contain" a substring.
72+
await page.getByRole('link', { name: 'app logo Progress' }).click();
73+
await page.getByRole('link', { name: 'Log In' }).click();
74+
await page.getByRole('textbox', { name: 'Email' }).click();
75+
await page
76+
.getByRole('textbox', { name: 'Email' })
77+
.fill('alice3@gatech.edu');
78+
await page.getByRole('textbox', { name: 'Email' }).press('Tab');
79+
await page.getByRole('textbox', { name: 'Password' }).fill('12345678');
80+
await page.getByRole('button', { name: 'Log In' }).click();
81+
82+
const dataLoaded = page.waitForRequest('/api/training-log/*');
83+
await page.getByRole('link', { name: 'edit training log' }).click();
84+
85+
await dataLoaded;
86+
await page.getByRole('textbox', { name: 'Title' }).click();
87+
await page.waitForTimeout(5000);
88+
await page
89+
.getByRole('textbox', { name: 'Title' })
90+
.fill('Running Practice');
91+
await page.getByRole('button', { name: 'Save' }).click();
92+
await expect(page.locator('[id="1"]')).toContainText(
93+
'Training log updated successfully.'
94+
);
95+
await page
96+
.getByRole('link', { name: 'Training Logs icon Training' })
97+
.click();
98+
await expect(page.locator('h3')).toContainText('Running Practice');
99+
});
100+
});

‎package-lock.json‎

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"dependencies": {
1212
"argon2": "^0.44.0",
1313
"axios": "^1.13.2",
14+
"dotenv": "^17.2.3",
1415
"jose": "^6.1.2",
1516
"mongoose": "^8.19.4",
1617
"next": "16.0.1",

0 commit comments

Comments
 (0)