feat: add daily home and vitals dashboard prototypes
This commit is contained in:
@@ -41,7 +41,7 @@ test('server functions also reject foreign identity and cross-site calls', async
|
||||
const serverRequest = page.waitForRequest((request) =>
|
||||
request.url().includes('/_serverFn/'),
|
||||
)
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
const url = (await serverRequest).url()
|
||||
expect(
|
||||
(
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, test } from '../fixtures'
|
||||
test('opens account management after hydration without inheriting real OAuth configuration', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
await expect(
|
||||
page.getByRole('button', { name: '名前を変更', includeHidden: true }),
|
||||
).toBeEnabled()
|
||||
@@ -51,7 +51,7 @@ test('rejects an unbound OAuth callback without contacting an instance', async (
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/oauth/mastodon/callback?state=invalid&code=not-a-real-code')
|
||||
await expect(page).toHaveURL(/\/\?mastodon=failed$/)
|
||||
await expect(page).toHaveURL(/\/deck\?mastodon=failed$/)
|
||||
await expect(page.getByRole('alert')).toContainText(
|
||||
'Mastodonの認可を完了できませんでした。',
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ async function addColumn(
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'デッキとして保存', exact: true }),
|
||||
).toBeEnabled()
|
||||
@@ -301,7 +301,7 @@ test('keeps an open draft through remote edits and rejects its stale save', asyn
|
||||
})
|
||||
try {
|
||||
const device = await other.newPage()
|
||||
await device.goto('/')
|
||||
await device.goto('/deck')
|
||||
await page.getByRole('button', { name: '名前を変更' }).click()
|
||||
await page.getByLabel('調査テーマ・デッキ名').fill('編集中の名前')
|
||||
await device.getByRole('button', { name: '名前を変更' }).click()
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { expect, test } from '../fixtures'
|
||||
|
||||
test('previews a natural-language memo and shares the executed record with journal', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
const memo = '明日のレビューは導線から確認する'
|
||||
await page
|
||||
.getByRole('textbox', { name: 'ホームへの入力' })
|
||||
.fill(`メモ: ${memo}`)
|
||||
await page.getByRole('button', { name: '候補を表示' }).click()
|
||||
const execute = page.getByRole('button', { name: '実行する', exact: true })
|
||||
await expect(execute).toBeVisible()
|
||||
await execute.click()
|
||||
await page.getByRole('link', { name: '記録', exact: true }).first().click()
|
||||
await expect(page.getByRole('article', { name: 'メモの記録' })).toContainText(
|
||||
memo,
|
||||
)
|
||||
await page.getByRole('link', { name: 'ホーム', exact: true }).first().click()
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'ホームへの入力' }),
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('keeps the home usable on narrow screens', async ({
|
||||
page,
|
||||
a11y,
|
||||
}, testInfo) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
for (const width of [320, 375, 768]) {
|
||||
await page.setViewportSize({ width, height: 850 })
|
||||
await expect(
|
||||
page.getByRole('textbox', { name: 'ホームへの入力' }),
|
||||
).toBeVisible()
|
||||
expect(
|
||||
await page.evaluate(() => document.documentElement.scrollWidth),
|
||||
).toBe(width)
|
||||
await page.screenshot({ path: testInfo.outputPath(`home-${width}.png`) })
|
||||
if (width === 320) expect((await a11y().analyze()).violations).toEqual([])
|
||||
}
|
||||
})
|
||||
|
||||
test('adds tasks only after confirmation and can defer and restore them', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
const input = page.getByRole('textbox', { name: 'ホームへの入力' })
|
||||
const complete = page.getByRole('button', {
|
||||
name: '買い物を完了',
|
||||
exact: true,
|
||||
})
|
||||
await input.fill('買い物を追加')
|
||||
await page.getByRole('button', { name: '候補を表示' }).click()
|
||||
await expect(complete).toHaveCount(0)
|
||||
await page.getByRole('button', { name: '実行する', exact: true }).click()
|
||||
await expect(complete).toBeVisible()
|
||||
await input.fill('買い物をあとで')
|
||||
await page.getByRole('button', { name: '候補を表示' }).click()
|
||||
await expect(complete).toBeVisible()
|
||||
await page.getByRole('button', { name: '実行する', exact: true }).click()
|
||||
await expect(complete).toHaveCount(0)
|
||||
await page.locator('.home-rest summary').click()
|
||||
await page.getByRole('button', { name: '買い物を戻す', exact: true }).click()
|
||||
await complete.click()
|
||||
await expect(complete).toHaveCount(0)
|
||||
await page.locator('.home-rest summary').click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: '買い物を戻す', exact: true }),
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('resolves communication within home and carries the status to the shared queue', async ({
|
||||
page,
|
||||
a11y,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.getByRole('button', { name: '青木 遥との会話を開く' }).click()
|
||||
const dialog = page.getByRole('dialog', { name: '青木 遥との連絡' })
|
||||
await expect(
|
||||
dialog.getByRole('textbox', { name: '返信', exact: true }),
|
||||
).toBeVisible()
|
||||
expect((await a11y().analyze()).violations).toEqual([])
|
||||
await dialog.getByRole('button', { name: '今は置いておく' }).click()
|
||||
await expect(dialog).toHaveCount(0)
|
||||
await expect(
|
||||
page.getByRole('button', { name: '青木 遥との会話を開く' }),
|
||||
).toHaveCount(0)
|
||||
await page.getByRole('link', { name: '連絡', exact: true }).first().click()
|
||||
const rows = page.locator('.support-row')
|
||||
await expect(
|
||||
rows.filter({ hasText: '来週のレビュー、日程を決める' }),
|
||||
).toHaveCount(0)
|
||||
await page.getByRole('button', { name: 'あとで', exact: true }).click()
|
||||
await rows.filter({ hasText: '来週のレビュー、日程を決める' }).click()
|
||||
await page.getByRole('button', { name: '今に戻す' }).click()
|
||||
const back = page.getByRole('button', { name: '連絡一覧に戻る' })
|
||||
if (await back.isVisible()) await back.click()
|
||||
await page.getByRole('link', { name: 'ホーム', exact: true }).first().click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: '青木 遥との会話を開く' }),
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('updates the prose brief as priorities change and opens the referenced conversation', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.waitForLoadState('networkidle')
|
||||
const brief = page.getByRole('region', { name: '今日のブリーフ' })
|
||||
await expect(brief).toContainText('レビュー資料を確認する')
|
||||
await expect(brief).toContainText('4件')
|
||||
await page
|
||||
.getByRole('button', { name: 'レビュー資料を確認するを完了', exact: true })
|
||||
.click()
|
||||
await expect(brief).not.toContainText('レビュー資料を確認する')
|
||||
await brief.getByRole('button', { name: '青木 遥さんへの返信' }).click()
|
||||
const dialog = page.getByRole('dialog', {
|
||||
name: '青木 遥との連絡',
|
||||
exact: true,
|
||||
})
|
||||
await dialog.getByRole('button', { name: '今は置いておく' }).click()
|
||||
await expect(brief).toContainText('3件')
|
||||
await expect(brief).not.toContainText('青木 遥')
|
||||
await page
|
||||
.getByRole('textbox', { name: 'ホームへの入力' })
|
||||
.fill('再開地点:資料の2ページ目から')
|
||||
await page.getByRole('button', { name: '候補を表示' }).click()
|
||||
await page.getByRole('button', { name: '実行する', exact: true }).click()
|
||||
await expect(brief).toContainText('資料の2ページ目から')
|
||||
})
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
test('pushes the current research snapshot on each SSE connection', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
const snapshots = await page.evaluate(async () => {
|
||||
const connect = () =>
|
||||
new Promise<unknown>((resolve, reject) => {
|
||||
@@ -41,7 +41,7 @@ test('pushes the current research snapshot on each SSE connection', async ({
|
||||
test('restores the conversation and growing temporary deck from reconnect snapshots', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
await showDeckManagement(page)
|
||||
await page.getByRole('button', { name: 'カラムを追加', exact: true }).click()
|
||||
const accounts = page.getByRole('combobox', {
|
||||
@@ -215,7 +215,7 @@ test('renders Markdown and reveals an unloaded citation in an offscreen deck col
|
||||
a11y,
|
||||
}) => {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' })
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
await showDeckManagement(page)
|
||||
await page.getByRole('button', { name: 'カラムを追加', exact: true }).click()
|
||||
const accounts = page.getByRole('combobox', {
|
||||
@@ -322,7 +322,7 @@ test('explains unconfigured Codex and prevents starting research without affecti
|
||||
page,
|
||||
a11y,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
const heading = page.getByRole('heading', { level: 1 })
|
||||
await expect(heading).toBeVisible()
|
||||
const originalTitle = await heading.textContent()
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import { expect, test } from '../fixtures'
|
||||
|
||||
test('filters body systems and opens lab history', async ({ page, a11y }) => {
|
||||
await page.goto('/vitals')
|
||||
await page.waitForLoadState('networkidle')
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Body', exact: true }),
|
||||
).toBeVisible()
|
||||
const systems = page.locator('.vital-system-card')
|
||||
await expect(systems).toHaveCount(8)
|
||||
await page
|
||||
.locator('.vital-body-page > .vital-tabs')
|
||||
.getByRole('button', { name: 'Cardiovascular', exact: true })
|
||||
.click()
|
||||
await expect(systems).toHaveCount(1)
|
||||
await systems.first().click()
|
||||
const dialog = page.getByRole('dialog', { name: 'Cardiovascular' })
|
||||
await expect(dialog).toContainText('68')
|
||||
await dialog
|
||||
.getByRole('button', { name: '閉じる', exact: true })
|
||||
.last()
|
||||
.click()
|
||||
await page.getByRole('button', { name: 'Lab history', exact: true }).click()
|
||||
await expect(page.locator('.vital-history')).toContainText('Jul 28')
|
||||
await expect(page.locator('.vital-history')).toContainText('93')
|
||||
await page.getByRole('button', { name: 'Systems', exact: true }).click()
|
||||
await page
|
||||
.locator('.vital-body-page > .vital-tabs')
|
||||
.getByRole('button', { name: 'Overview', exact: true })
|
||||
.click()
|
||||
await expect(systems).toHaveCount(8)
|
||||
expect((await a11y().analyze()).violations).toEqual([])
|
||||
})
|
||||
|
||||
test('explores environment charts and simulated climate controls', async ({
|
||||
page,
|
||||
a11y,
|
||||
}) => {
|
||||
await page.goto('/vitals')
|
||||
await page.waitForLoadState('networkidle')
|
||||
const menu = page.getByRole('button', { name: 'Healthメニューを開く' })
|
||||
if (await menu.isVisible()) await menu.click()
|
||||
await page
|
||||
.getByRole('complementary', { name: 'Health navigation' })
|
||||
.getByRole('button', { name: 'Environment', exact: true })
|
||||
.click()
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Environment', exact: true }),
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: '7D', exact: true }).click()
|
||||
await page
|
||||
.getByRole('group', { name: 'Trend metric' })
|
||||
.getByRole('button', { name: 'CO₂', exact: true })
|
||||
.click()
|
||||
await expect(
|
||||
page.getByRole('img', { name: '7D CO₂ simulated environmental readings' }),
|
||||
).toBeVisible()
|
||||
await page
|
||||
.locator('.vital-metric')
|
||||
.filter({ hasText: 'Room temperature' })
|
||||
.click()
|
||||
const dialog = page.getByRole('dialog', { name: 'Room temperature' })
|
||||
await expect(dialog).toContainText('64.0')
|
||||
await dialog.getByRole('button', { name: 'Back to overview' }).click()
|
||||
await page.getByRole('button', { name: 'Sync now', exact: true }).click()
|
||||
await expect(
|
||||
page.getByRole('status').filter({ hasText: 'Sample readings refreshed' }),
|
||||
).toBeVisible()
|
||||
expect((await a11y().analyze()).violations).toEqual([])
|
||||
await page
|
||||
.getByRole('button', { name: 'Climate control', exact: true })
|
||||
.click()
|
||||
await page.getByRole('button', { name: 'Cool', exact: true }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Cool', exact: true }),
|
||||
).toHaveAttribute('aria-pressed', 'true')
|
||||
const temperature = page.getByRole('slider', { name: 'Target temperature' })
|
||||
await temperature.focus()
|
||||
await temperature.press('ArrowRight')
|
||||
await expect(temperature).toHaveValue('69')
|
||||
await page.getByRole('button', { name: 'Air purifier · On' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Air purifier · Off' }),
|
||||
).toHaveAttribute('aria-pressed', 'false')
|
||||
})
|
||||
|
||||
test('keeps both dashboards within the viewport', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await page.goto('/vitals')
|
||||
await page.waitForLoadState('networkidle')
|
||||
for (const width of [375, 768, 1440]) {
|
||||
await page.setViewportSize({ width, height: 1000 })
|
||||
expect(
|
||||
await page.evaluate(() => document.documentElement.scrollWidth),
|
||||
).toBe(width)
|
||||
await page.screenshot({ path: testInfo.outputPath(`body-${width}.png`) })
|
||||
const menu = page.getByRole('button', { name: 'Healthメニューを開く' })
|
||||
if (await menu.isVisible()) await menu.click()
|
||||
const navigation = page.getByRole('complementary', {
|
||||
name: 'Health navigation',
|
||||
})
|
||||
await navigation
|
||||
.getByRole('button', { name: 'Environment', exact: true })
|
||||
.click()
|
||||
expect(
|
||||
await page.evaluate(() => document.documentElement.scrollWidth),
|
||||
).toBe(width)
|
||||
await page.screenshot({
|
||||
path: testInfo.outputPath(`environment-${width}.png`),
|
||||
})
|
||||
if (await menu.isVisible()) await menu.click()
|
||||
await navigation.getByRole('button', { name: 'Body', exact: true }).click()
|
||||
}
|
||||
})
|
||||
@@ -64,7 +64,7 @@ let pageErrors: string[]
|
||||
test.beforeEach(async ({ page }) => {
|
||||
pageErrors = []
|
||||
page.on('pageerror', (error) => pageErrors.push(error.message))
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
await expect
|
||||
.poll(() => toolNames(page))
|
||||
.toEqual([
|
||||
@@ -141,7 +141,7 @@ test('creates temporary research, edits it, persists explicitly and reopens it o
|
||||
})
|
||||
try {
|
||||
const device = await other.newPage()
|
||||
await device.goto('/')
|
||||
await device.goto('/deck')
|
||||
await expect(device.getByRole('heading', { level: 1 })).toHaveText(
|
||||
'更新した調査',
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ test('keeps the sidebar, composer and deck reachable at compact widths', async (
|
||||
page,
|
||||
a11y,
|
||||
}, testInfo) => {
|
||||
await page.goto('/')
|
||||
await page.goto('/deck')
|
||||
for (const width of [320, 375, 414, 768]) {
|
||||
await page.setViewportSize({ width, height: 850 })
|
||||
await showResearch(page)
|
||||
|
||||
Reference in New Issue
Block a user