feat: roomier BO2 companion with +/- rank stepping
All checks were successful
Test and deploy staging / staging (push) Successful in 2m29s
All checks were successful
Test and deploy staging / staging (push) Successful in 2m29s
- Scale the COD page to the screen: larger gun art, text and progress ribbon, sized from one viewport unit so it fills a MacBook Pro 16" and big monitors and still fits 1280x720 without scrolling. - Five columns on wide screens and four below, picked by CSS, so the server-rendered page already has the final layout. - Replace the tabs/filters/overview rows with one toolbar of matching controls. - Player and weapon ranks step with + and -: level 55 + 1 is the next prestige at level 1, up to max prestige. Presses show at once, batch while a save is running, and stepRank does the arithmetic in SQL. - Clicking a rank still opens exact entry for big jumps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -222,7 +222,7 @@ test('weapon/player ranks, challenge progress and osu goals/scores/training pers
|
||||
await sql`select c.id,c.target from tracker_challenges c join tracker_games g on c.game_id=g.id where g.slug='black-ops-3' and c.target>1 limit 1`;
|
||||
const cod = '/projects/game-tracker/call-of-duty/black-ops-3';
|
||||
await page.goto(cod);
|
||||
await page.getByRole('button', { name: 'Update my rank' }).click();
|
||||
await page.getByRole('button', { name: 'Player level and prestige' }).click();
|
||||
await page.getByLabel('Current level', { exact: true }).fill('42');
|
||||
await page.getByLabel('Prestige', { exact: true }).fill('3');
|
||||
await page.getByRole('button', { name: 'Save rank' }).click();
|
||||
@@ -537,7 +537,12 @@ test('live weapon controls save immediately, infer max level from prestige, and
|
||||
await expect(level).toHaveText(/LEVEL2/);
|
||||
await card.getByRole('button', { name: `Level down ${weapon.name}` }).click();
|
||||
await expect(level).toHaveText(/LEVEL1/);
|
||||
await card.getByRole('button', { name: `Prestige up ${weapon.name}` }).click();
|
||||
// Without known caps + / − only move the level; prestige is typed into the exact-rank dialog.
|
||||
await level.click();
|
||||
await page.getByLabel('Prestige', { exact: true }).fill('1');
|
||||
await page.getByRole('button', { name: 'Save rank' }).click();
|
||||
await expect(page.getByRole('dialog')).toHaveCount(0);
|
||||
await page.getByRole('button', { name: `${weapon.name} camos and milestones` }).click();
|
||||
await expect(card).toContainText('Max level reached');
|
||||
await expect(card.getByRole('checkbox', { name: /max level/ })).toHaveCount(0);
|
||||
expect(
|
||||
@@ -547,38 +552,28 @@ test('live weapon controls save immediately, infer max level from prestige, and
|
||||
).toMatchObject({ level: 1, prestige: 1 });
|
||||
const concurrent = await Promise.all(
|
||||
Array.from({ length: 4 }, () =>
|
||||
action(page.request, cod + '?/adjustWeaponRank', {
|
||||
itemId: String(weapon.id),
|
||||
field: 'level',
|
||||
delta: '1'
|
||||
})
|
||||
action(page.request, cod + '?/stepRank', { itemId: String(weapon.id), delta: '1' })
|
||||
)
|
||||
);
|
||||
expect(concurrent.every((r) => r.status === 200)).toBe(true);
|
||||
await page.reload();
|
||||
await page.getByRole('button', { name: `${weapon.name} camos and milestones` }).click();
|
||||
await expect(level).toHaveText(/LEVEL5/);
|
||||
await page.route('**/*adjustWeaponRank', (route) => route.abort());
|
||||
await page.route('**/*stepRank', (route) => route.abort());
|
||||
await card.getByRole('button', { name: `Level up ${weapon.name}` }).click();
|
||||
await expect(card.getByRole('status')).toContainText(/fetch|save|network/i);
|
||||
await expect(level).toHaveText(/LEVEL5/);
|
||||
await page.unroute('**/*adjustWeaponRank');
|
||||
await page.unroute('**/*stepRank');
|
||||
await card.getByRole('button', { name: `Level up ${weapon.name}` }).click();
|
||||
await expect(level).toHaveText(/LEVEL6/);
|
||||
for (const delta of ['999', '0', '1.5', ''])
|
||||
expect(
|
||||
(await action(page.request, cod + '?/stepRank', { itemId: String(weapon.id), delta })).status
|
||||
).toBe(400);
|
||||
expect(
|
||||
(
|
||||
await action(page.request, cod + '?/adjustWeaponRank', {
|
||||
await action(page.request, '/projects/game-tracker/call-of-duty/wwii?/stepRank', {
|
||||
itemId: String(weapon.id),
|
||||
field: 'level',
|
||||
delta: '999'
|
||||
})
|
||||
).status
|
||||
).toBe(400);
|
||||
expect(
|
||||
(
|
||||
await action(page.request, '/projects/game-tracker/call-of-duty/wwii?/adjustWeaponRank', {
|
||||
itemId: String(weapon.id),
|
||||
field: 'level',
|
||||
delta: '1'
|
||||
})
|
||||
).status
|
||||
@@ -586,9 +581,8 @@ test('live weapon controls save immediately, infer max level from prestige, and
|
||||
const anonymous = await playwright.request.newContext({ baseURL: origin });
|
||||
expect(
|
||||
(
|
||||
await action(anonymous, cod + '?/adjustWeaponRank', {
|
||||
await action(anonymous, cod + '?/stepRank', {
|
||||
itemId: String(weapon.id),
|
||||
field: 'level',
|
||||
delta: '1'
|
||||
})
|
||||
).status
|
||||
@@ -911,9 +905,8 @@ test('BO2 armoury fits every gun, enforces caps and advances all rank progress w
|
||||
const overall = page.getByRole('progressbar', { name: 'Game completion', exact: true });
|
||||
await expect(levels).toHaveAttribute('aria-valuenow', '0');
|
||||
await card.getByRole('button', { name: 'Level up M27', exact: true }).click();
|
||||
await expect(card.getByRole('button', { name: 'Edit M27 level and prestige' })).toHaveText(
|
||||
'LEVEL2/17'
|
||||
);
|
||||
const m27Rank = card.getByRole('button', { name: 'Edit M27 level and prestige' });
|
||||
await expect(m27Rank).toHaveText('P0 2/17');
|
||||
const first = Number(await levels.getAttribute('aria-valuenow'));
|
||||
expect(first).toBeGreaterThan(0);
|
||||
const before = Number(await overall.getAttribute('aria-valuenow'));
|
||||
@@ -942,37 +935,51 @@ test('BO2 armoury fits every gun, enforces caps and advances all rank progress w
|
||||
})
|
||||
).status
|
||||
).toBe(400);
|
||||
expect(
|
||||
(
|
||||
await action(page.request, cod + '?/adjustWeaponRank', {
|
||||
itemId: String(m27.id),
|
||||
field: 'prestige',
|
||||
delta: '1'
|
||||
})
|
||||
).status
|
||||
).toBe(400);
|
||||
await action(page.request, cod + '?/saveRank', {
|
||||
itemId: String(m27.id),
|
||||
level: '17',
|
||||
prestige: '0'
|
||||
});
|
||||
// Presses from two devices at max level both count: level 17 → prestige 1 level 2.
|
||||
const simultaneous = await Promise.all(
|
||||
[1, 2].map(() =>
|
||||
action(page.request, cod + '?/adjustWeaponRank', {
|
||||
itemId: String(m27.id),
|
||||
field: 'prestige',
|
||||
delta: '1'
|
||||
})
|
||||
action(page.request, cod + '?/stepRank', { itemId: String(m27.id), delta: '1' })
|
||||
)
|
||||
);
|
||||
expect(simultaneous.filter((r) => r.status === 200)).toHaveLength(1);
|
||||
expect(simultaneous.every((r) => r.status === 200)).toBe(true);
|
||||
expect(
|
||||
(
|
||||
await sql`select level,prestige from tracker_weapon_ranks where user_id=${userId} and item_id=${m27.id}`
|
||||
)[0]
|
||||
).toMatchObject({ level: 1, prestige: 1 });
|
||||
).toMatchObject({ level: 2, prestige: 1 });
|
||||
await page.reload();
|
||||
await expect(card).toContainText('18/51');
|
||||
await expect(m27Rank).toHaveText('P1 2/17');
|
||||
await card.getByRole('button', { name: 'Level down M27', exact: true }).click();
|
||||
await card.getByRole('button', { name: 'Level down M27', exact: true }).click();
|
||||
await expect(m27Rank).toHaveText('P0 17/17');
|
||||
await expect
|
||||
.poll(
|
||||
async () =>
|
||||
(
|
||||
await sql`select level,prestige from tracker_weapon_ranks where user_id=${userId} and item_id=${m27.id}`
|
||||
)[0]
|
||||
)
|
||||
.toMatchObject({ level: 17, prestige: 0 });
|
||||
await card.getByRole('button', { name: 'Level up M27', exact: true }).click();
|
||||
await expect(m27Rank).toHaveText('P1 1/17');
|
||||
await action(page.request, cod + '?/saveRank', { level: '55', prestige: '0' });
|
||||
await page.reload();
|
||||
const badge = page.getByLabel('Player level and prestige');
|
||||
await page.getByRole('button', { name: 'Level up player rank', exact: true }).click();
|
||||
await expect(badge).toContainText('PRESTIGE1/10');
|
||||
await expect(badge).toContainText('LEVEL1/55');
|
||||
await page.getByRole('button', { name: 'Level down player rank', exact: true }).click();
|
||||
await expect(badge).toContainText('LEVEL55/55');
|
||||
await expect
|
||||
.poll(
|
||||
async () => (await sql`select level,prestige from tracker_ranks where user_id=${userId}`)[0]
|
||||
)
|
||||
.toMatchObject({ level: 55, prestige: 0 });
|
||||
await action(page.request, cod + '?/saveRank', {
|
||||
itemId: String(m27.id),
|
||||
level: '17',
|
||||
@@ -980,7 +987,7 @@ test('BO2 armoury fits every gun, enforces caps and advances all rank progress w
|
||||
});
|
||||
await action(page.request, cod + '?/saveRank', { level: '42', prestige: '3' });
|
||||
await page.reload();
|
||||
await expect(card).toContainText('51/51');
|
||||
await expect(m27Rank).toHaveText('P2 17/17');
|
||||
await expect(card.getByRole('button', { name: 'Level up M27' })).toBeDisabled();
|
||||
await expect(page.getByRole('img', { name: 'Prestige 3 emblem' })).toBeVisible();
|
||||
const progress = await overall.getAttribute('aria-valuenow');
|
||||
|
||||
Reference in New Issue
Block a user