Skip to content

Commit

Permalink
chore: update dependencies and switch to pnpm for package management (#…
Browse files Browse the repository at this point in the history
…41)

* test: enhance tests, disable firefox and webkit browsers in Playwright setup
  • Loading branch information
fityannugroho authored Nov 12, 2024
1 parent 5939c48 commit a7e90a1
Show file tree
Hide file tree
Showing 9 changed files with 7,208 additions and 10,430 deletions.
29 changes: 18 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,46 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache: 'pnpm'
- name: Install dependencies
run: npm ci
run: pnpm install
- name: Check code style
run: npm run lint
run: pnpm run lint
- name: Run unit tests
run: npm test
run: pnpm test
- name: Build project
run: npm run build
run: pnpm run build

e2e:
needs: build
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: 'npm'
cache: 'pnpm'
- name: Install dependencies
run: npm ci
run: pnpm install
- name: Build project
run: npm run build
run: pnpm run build
- name: Install Playwright Browsers
run: npx playwright install --with-deps
run: pnpm exec playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
run: pnpm exec playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ The map of Indonesia's administrative areas.
## Installation

1. Clone this repository
1. Install dependencies: `npm install`
1. Run the app: `npm run dev`
1. Install dependencies: `pnpm install`
1. Run the app: `pnpm run dev`
1. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

## License
Expand Down
68 changes: 36 additions & 32 deletions app/__tests__/dashboard/area-selector.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { config } from '@/lib/config'

Check failure on line 1 in app/__tests__/dashboard/area-selector.spec.ts

View workflow job for this annotation

GitHub Actions / e2e

[chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district

1) [chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Test timeout of 30000ms exceeded.
import { expect, test } from '@playwright/test'

test('province selector should load all provinces', async ({ page }) => {
test.beforeEach(async ({ page }) => {
const res = await page.request.get(config.dataSource.area.url)
await expect(res).toBeOK()

Check failure on line 6 in app/__tests__/dashboard/area-selector.spec.ts

View workflow job for this annotation

GitHub Actions / e2e

[chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district

1) [chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district Error: expect(received).toBeOK() Call log: - → GET https://idn-area.up.railway.app/ - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36 - accept: */* - accept-encoding: gzip,deflate,br - ← 429 Too Many Requests - access-control-allow-origin: * - content-length: 68 - content-type: application/json; charset=utf-8 - date: Tue, 12 Nov 2024 09:35:59 GMT - retry-after: 60 - server: railway-edge - x-railway-request-id: VeNZaYPZQDu9Q69Hyh5zwg_603524580 Response text: {"statusCode":429,"message":"ThrottlerException: Too Many Requests"} 4 | test.beforeEach(async ({ page }) => { 5 | const res = await page.request.get(config.dataSource.area.url) > 6 | await expect(res).toBeOK() | ^ 7 | 8 | await page.goto('/') 9 | }) at /home/runner/work/idn-area-map/idn-area-map/app/__tests__/dashboard/area-selector.spec.ts:6:21

Check failure on line 6 in app/__tests__/dashboard/area-selector.spec.ts

View workflow job for this annotation

GitHub Actions / e2e

[chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district

1) [chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: expect(received).toBeOK() Call log: - → GET https://idn-area.up.railway.app/ - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.31 Safari/537.36 - accept: */* - accept-encoding: gzip,deflate,br - ← 429 Too Many Requests - access-control-allow-origin: * - content-length: 68 - content-type: application/json; charset=utf-8 - date: Tue, 12 Nov 2024 09:36:31 GMT - retry-after: 29 - server: railway-edge - x-railway-request-id: szYO8CNMTs-uy3nbco8RIA_603524580 Response text: {"statusCode":429,"message":"ThrottlerException: Too Many Requests"} 4 | test.beforeEach(async ({ page }) => { 5 | const res = await page.request.get(config.dataSource.area.url) > 6 | await expect(res).toBeOK() | ^ 7 | 8 | await page.goto('/') 9 | }) at /home/runner/work/idn-area-map/idn-area-map/app/__tests__/dashboard/area-selector.spec.ts:6:21

await page.goto('/')
})

test('province selector should load all provinces', async ({ page }) => {
await expect(page.getByRole('button', { name: 'Province' })).toBeVisible()
await page.getByRole('button', { name: 'Province' }).click()
await expect(page.getByPlaceholder('Search Province')).toBeVisible()
Expand All @@ -12,7 +18,6 @@ test('province selector should load all provinces', async ({ page }) => {
})

test('regency selector should load some regencies', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('button', { name: 'Regency' })).toBeVisible()
await page.getByRole('button', { name: 'Regency' }).click()
await expect(page.getByPlaceholder('Search Regency')).toBeVisible()
Expand All @@ -24,7 +29,6 @@ test('regency selector should load some regencies', async ({ page }) => {
})

test('district selector should load some districts', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('button', { name: 'District' })).toBeVisible()
await page.getByRole('button', { name: 'District' }).click()
await expect(page.getByPlaceholder('Search District')).toBeVisible()
Expand All @@ -36,7 +40,6 @@ test('district selector should load some districts', async ({ page }) => {
})

test('village selector should load some villages', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('button', { name: 'Village' })).toBeVisible()
await page.getByRole('button', { name: 'Village' }).click()
await expect(page.getByPlaceholder('Search Village')).toBeVisible()
Expand All @@ -48,108 +51,109 @@ test('village selector should load some villages', async ({ page }) => {
})

test('search province should show the correct provinces', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'Province' }).click()
await page.getByPlaceholder('Search Province').fill('jawa')

// Ensure all options contains 'jawa' word (case-insensitive)
for (const option of await page.getByRole('option').all()) {
const text = await option.textContent()
expect(text).toMatch(/jawa/i)
await expect(option).toContainText(/jawa/i)
}
})

test('search regency should show the correct regencies', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'Regency' }).click()
await page.getByPlaceholder('Search Regency').fill('bandung')

// Ensure all options contains 'bandung' word (case-insensitive)
for (const option of await page.getByRole('option').all()) {
const text = await option.textContent()
expect(text).toMatch(/bandung/i)
await expect(option).toContainText(/bandung/i)
}
})

test('search district should show the correct districts', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'District' }).click()
await page.getByPlaceholder('Search District').fill('cirebon')

// Ensure all options contains 'cirebon' word (case-insensitive)
for (const option of await page.getByRole('option').all()) {
const text = await option.textContent()
expect(text).toMatch(/cirebon/i)
await expect(option).toContainText(/cirebon/i)
}
})

test('search village should show the correct villages', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'Village' }).click()
await page.getByPlaceholder('Search Village').fill('pabean')

// Ensure all options contains 'pabean' word (case-insensitive)
for (const option of await page.getByRole('option').all()) {
const text = await option.textContent()
expect(text).toMatch(/pabean/i)
await expect(option).toContainText(/pabean/i)
}
})

test('should load all regencies of the selected province', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'Province' }).click()

const provinceCode = (
await page.getByRole('option', { name: 'Bali' }).getAttribute('data-value')
await page
.getByRole('option', { name: 'jakarta' })
.getAttribute('data-value')
)?.split('_')[0]
await page.getByRole('option', { name: 'Bali' }).click()
await page.getByRole('option', { name: 'jakarta' }).click()

await expect(page.getByRole('button', { name: 'Regency' })).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Regency' })).toBeEnabled({
timeout: 10_000,
})
await page.getByRole('button', { name: 'Regency' }).click()

// Ensure that the value of each option starts with the selected province code
for (const option of await page.getByRole('option').all()) {
const value = await option.getAttribute('data-value')
expect(value).toMatch(new RegExp(`^${provinceCode}`))
await expect(option).toHaveAttribute(
'data-value',
new RegExp(`^${provinceCode}`),
)
}
})

test('should load all districts of the selected regency', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'Regency' }).click()

const regencyCode = (
await page.getByRole('option').first().getAttribute('data-value')
)?.split('_')[0]
await page.getByRole('option').first().click()

await expect(
page.getByRole('button', { name: 'District' }),
).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'District' })).toBeEnabled({
timeout: 10_000,
})
await page.getByRole('button', { name: 'District' }).click()

// Ensure that the value of each option starts with the selected regency code
for (const option of await page.getByRole('option').all()) {
const value = await option.getAttribute('data-value')
expect(value).toMatch(new RegExp(`^${regencyCode}`))
await expect(option).toHaveAttribute(
'data-value',
new RegExp(`^${regencyCode}`),
)
}
})

test('should load all villages of the selected district', async ({ page }) => {
await page.goto('/')
await page.getByRole('button', { name: 'District' }).click()

const districtCode = (
await page.getByRole('option').first().getAttribute('data-value')

Check failure on line 143 in app/__tests__/dashboard/area-selector.spec.ts

View workflow job for this annotation

GitHub Actions / e2e

[chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district

1) [chromium] › dashboard/area-selector.spec.ts:139:5 › should load all villages of the selected district Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: locator.getAttribute: Test timeout of 30000ms exceeded. Call log: - waiting for getByRole('option').first() 141 | 142 | const districtCode = ( > 143 | await page.getByRole('option').first().getAttribute('data-value') | ^ 144 | )?.split('_')[0] 145 | await page.getByRole('option').first().click() 146 | at /home/runner/work/idn-area-map/idn-area-map/app/__tests__/dashboard/area-selector.spec.ts:143:44
)?.split('_')[0]
await page.getByRole('option').first().click()

await expect(page.getByRole('button', { name: 'Village' })).not.toBeDisabled()
await expect(page.getByRole('button', { name: 'Village' })).toBeEnabled({
timeout: 10_000,
})
await page.getByRole('button', { name: 'Village' }).click()

// Ensure that the value of each option starts with the selected district code
for (const option of await page.getByRole('option').all()) {
const value = await option.getAttribute('data-value')
expect(value).toMatch(new RegExp(`^${districtCode}`))
await expect(option).toHaveAttribute(
'data-value',
new RegExp(`^${districtCode}`),
)
}
})
35 changes: 21 additions & 14 deletions app/__tests__/dashboard/boundary-settings.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { config } from '@/lib/config'
import test, { expect } from '@playwright/test'

test.beforeEach(async ({ page }) => {
const res = await page.request.get(config.dataSource.area.url)
await expect(res).toBeOK()

await page.goto('/')
})

test('should show the boundary settings, enabled by default', async ({
page,
}) => {
await page.goto('/')
await expect(page.getByLabel('Province')).toBeChecked()
await expect(page.getByLabel('Regency')).toBeChecked()
await expect(page.getByLabel('District')).toBeChecked()
await expect(page.getByLabel('Village')).toBeChecked()
})

test('should hide the province boundary when disabled', async ({ page }) => {
await page.goto('/')

// Select a province
await page.getByRole('button', { name: 'Province' }).click()
await page.getByRole('option', { name: 'BALI' }).click()
await expect(page.getByRole('main').locator('g path')).toBeVisible()
await page.getByRole('option', { name: 'jakarta' }).click()
await expect(page.getByRole('main').locator('g path')).toBeVisible({
timeout: 10_000,
})

// Disable the province boundary
await page.getByLabel('Province').click()
Expand All @@ -27,12 +34,12 @@ test('should hide the province boundary when disabled', async ({ page }) => {
})

test('should hide the regency boundary when disabled', async ({ page }) => {
await page.goto('/')

// Select a regency
await page.getByRole('button', { name: 'Regency' }).click()
await page.getByRole('option').first().click()
await expect(page.getByRole('main').locator('g path')).toBeVisible()
await expect(page.getByRole('main').locator('g path')).toBeVisible({
timeout: 10_000,
})

// Disable the regency boundary setting
await page.getByLabel('Regency').click()
Expand All @@ -43,12 +50,12 @@ test('should hide the regency boundary when disabled', async ({ page }) => {
})

test('should hide the district boundary when disabled', async ({ page }) => {
await page.goto('/')

// Select a district
await page.getByRole('button', { name: 'District' }).click()
await page.getByRole('option').first().click()
await expect(page.getByRole('main').locator('g path')).toBeVisible()
await expect(page.getByRole('main').locator('g path')).toBeVisible({
timeout: 10_000,
})

// Disable the district boundary setting
await page.getByLabel('District').click()
Expand All @@ -59,12 +66,12 @@ test('should hide the district boundary when disabled', async ({ page }) => {
})

test('should hide the village boundary when disabled', async ({ page }) => {
await page.goto('/')

// Select a village
await page.getByRole('button', { name: 'Village' }).click()
await page.getByRole('option').first().click()
await expect(page.getByRole('main').locator('g path')).toBeVisible()
await expect(page.getByRole('main').locator('g path')).toBeVisible({
timeout: 10_000,
})

// Disable the village boundary setting
await page.getByLabel('Village').click()
Expand Down
14 changes: 7 additions & 7 deletions app/__tests__/dashboard/theme-toggle.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { expect, test } from '@playwright/test'

test('dark mode', async ({ page }) => {
test.beforeEach(async ({ page }) => {
await page.goto('/')
await expect(page.getByText('Dark')).not.toBeVisible()
})

test('dark mode', async ({ page }) => {
await expect(page.getByText('Dark')).toBeHidden()

// Click on the theme toggle button
await page.getByTestId('theme-toggle').click()
Expand All @@ -15,8 +18,7 @@ test('dark mode', async ({ page }) => {
})

test('light mode', async ({ page }) => {
await page.goto('/')
await expect(page.getByText('Light')).not.toBeVisible()
await expect(page.getByText('Light')).toBeHidden()

// Click on the theme toggle button
await page.getByTestId('theme-toggle').click()
Expand All @@ -32,13 +34,11 @@ test('system default mode', async ({ page }) => {
// Emulate dark mode as the preferred color scheme
await page.emulateMedia({ colorScheme: 'dark' })

await page.goto('/')

// Set to Light mode first
await page.getByTestId('theme-toggle').click()
await page.getByText('Light').click()
await expect(page.locator('html')).toHaveClass('light')
await expect(page.getByText('Light')).not.toBeVisible()
await expect(page.getByText('Light')).toBeHidden()

// Open theme toggle again and set to System mode
await page.getByTestId('theme-toggle').click()
Expand Down
Loading

0 comments on commit a7e90a1

Please sign in to comment.