merge: 빌드 완료 오픈 공지 복원 반영
This commit is contained in:
@@ -1406,13 +1406,13 @@ export const adminRouter = router({
|
|||||||
install: input.install,
|
install: input.install,
|
||||||
requestedSource: input.sourceMode,
|
requestedSource: input.sourceMode,
|
||||||
releaseSource: { mode: sourceMode, ref: sourceRef },
|
releaseSource: { mode: sourceMode, ref: sourceRef },
|
||||||
...(input.publishSchedule && selectedScenario
|
...(selectedScenario && input.install.preopenAt && input.install.openAt
|
||||||
? {
|
? {
|
||||||
publicAnnouncement: {
|
publicAnnouncement: {
|
||||||
enabled: true,
|
enabled: input.publishSchedule,
|
||||||
scenarioId: selectedScenario.id,
|
scenarioId: selectedScenario.id,
|
||||||
scenarioTitle: selectedScenario.title,
|
scenarioTitle: selectedScenario.title,
|
||||||
scheduledAt: input.scheduledAt,
|
scheduledAt: input.scheduledAt ?? null,
|
||||||
preopenAt: input.install.preopenAt,
|
preopenAt: input.install.preopenAt,
|
||||||
openAt: input.install.openAt,
|
openAt: input.install.openAt,
|
||||||
turnTermMinutes: input.install.turnTermMinutes,
|
turnTermMinutes: input.install.turnTermMinutes,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ type PublicAutorunOption = (typeof PUBLIC_AUTORUN_OPTIONS)[number];
|
|||||||
|
|
||||||
export type LobbyUpcomingReset = {
|
export type LobbyUpcomingReset = {
|
||||||
phase: 'SCHEDULED' | 'PREPARING' | 'READY' | 'DELAYED';
|
phase: 'SCHEDULED' | 'PREPARING' | 'READY' | 'DELAYED';
|
||||||
scheduledAt: string;
|
scheduledAt: string | null;
|
||||||
preopenAt: string;
|
preopenAt: string;
|
||||||
openAt: string;
|
openAt: string;
|
||||||
scenarioId: number;
|
scenarioId: number;
|
||||||
@@ -215,7 +215,7 @@ export const resolveUpcomingResetAnnouncement = (
|
|||||||
if (operation.type !== 'RESET' || !['QUEUED', 'RUNNING', 'SUCCEEDED'].includes(operation.status)) return null;
|
if (operation.type !== 'RESET' || !['QUEUED', 'RUNNING', 'SUCCEEDED'].includes(operation.status)) return null;
|
||||||
const payload = asRecord(operation.payload);
|
const payload = asRecord(operation.payload);
|
||||||
const announcement = asRecord(payload?.publicAnnouncement);
|
const announcement = asRecord(payload?.publicAnnouncement);
|
||||||
if (!announcement || announcement.enabled !== true) return null;
|
if (!announcement || (announcement.enabled !== true && operation.status !== 'SUCCEEDED')) return null;
|
||||||
|
|
||||||
const scheduledAt = readDateTime(announcement.scheduledAt);
|
const scheduledAt = readDateTime(announcement.scheduledAt);
|
||||||
const preopenAt = readDateTime(announcement.preopenAt);
|
const preopenAt = readDateTime(announcement.preopenAt);
|
||||||
@@ -226,7 +226,7 @@ export const resolveUpcomingResetAnnouncement = (
|
|||||||
const defaultStatTotal = readFiniteNumber(announcement.defaultStatTotal);
|
const defaultStatTotal = readFiniteNumber(announcement.defaultStatTotal);
|
||||||
const autorunUser = readAutorun(announcement.autorunUser);
|
const autorunUser = readAutorun(announcement.autorunUser);
|
||||||
if (
|
if (
|
||||||
!scheduledAt ||
|
(announcement.enabled === true && !scheduledAt) ||
|
||||||
!preopenAt ||
|
!preopenAt ||
|
||||||
!openAt ||
|
!openAt ||
|
||||||
!Number.isInteger(scenarioId) ||
|
!Number.isInteger(scenarioId) ||
|
||||||
@@ -248,7 +248,7 @@ export const resolveUpcomingResetAnnouncement = (
|
|||||||
? 'DELAYED'
|
? 'DELAYED'
|
||||||
: operation.status === 'SUCCEEDED'
|
: operation.status === 'SUCCEEDED'
|
||||||
? 'READY'
|
? 'READY'
|
||||||
: operation.status === 'RUNNING' || nowMs >= new Date(scheduledAt).getTime()
|
: operation.status === 'RUNNING' || (scheduledAt && nowMs >= new Date(scheduledAt).getTime())
|
||||||
? 'PREPARING'
|
? 'PREPARING'
|
||||||
: 'SCHEDULED';
|
: 'SCHEDULED';
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -772,6 +772,35 @@ describe('admin operation API', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await harness.caller.admin.operations.requestReset({
|
||||||
|
profileName: 'che:2',
|
||||||
|
sourceMode: 'COMMIT',
|
||||||
|
sourceRef: 'HEAD',
|
||||||
|
install,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(harness.createdInputs[1]).toMatchObject({
|
||||||
|
type: 'RESET',
|
||||||
|
scheduledAt: undefined,
|
||||||
|
payload: {
|
||||||
|
install,
|
||||||
|
publicAnnouncement: {
|
||||||
|
enabled: false,
|
||||||
|
scenarioId: 1010,
|
||||||
|
scenarioTitle: expect.any(String),
|
||||||
|
scheduledAt: null,
|
||||||
|
preopenAt: install.preopenAt,
|
||||||
|
openAt: install.openAt,
|
||||||
|
turnTermMinutes: 60,
|
||||||
|
fictionMode: '가상',
|
||||||
|
npcMode: 0,
|
||||||
|
defaultStatTotal: expect.any(Number),
|
||||||
|
otherTextInfo: expect.any(String),
|
||||||
|
autorunUser: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
harness.caller.admin.operations.requestReset({
|
harness.caller.admin.operations.requestReset({
|
||||||
profileName: 'che:2',
|
profileName: 'che:2',
|
||||||
|
|||||||
@@ -96,10 +96,21 @@ describe('resolveUpcomingResetAnnouncement', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails closed for an unpublished or incomplete snapshot', () => {
|
it('keeps an unpublished snapshot hidden until a completed build is ready', () => {
|
||||||
const unpublished = buildOperation();
|
const unpublished = buildOperation();
|
||||||
unpublished.payload = { publicAnnouncement: { enabled: false } };
|
unpublished.payload = {
|
||||||
|
publicAnnouncement: {
|
||||||
|
...(unpublished.payload as { publicAnnouncement: Record<string, unknown> }).publicAnnouncement,
|
||||||
|
enabled: false,
|
||||||
|
scheduledAt: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
expect(resolveUpcomingResetAnnouncement(unpublished, new Date('2026-08-27T04:00:00.000Z'))).toBeNull();
|
expect(resolveUpcomingResetAnnouncement(unpublished, new Date('2026-08-27T04:00:00.000Z'))).toBeNull();
|
||||||
|
unpublished.status = 'SUCCEEDED';
|
||||||
|
expect(resolveUpcomingResetAnnouncement(unpublished, new Date('2026-08-27T04:00:00.000Z'))).toMatchObject({
|
||||||
|
phase: 'READY',
|
||||||
|
scheduledAt: null,
|
||||||
|
});
|
||||||
|
|
||||||
const incomplete = buildOperation();
|
const incomplete = buildOperation();
|
||||||
incomplete.payload = { publicAnnouncement: { enabled: true, scenarioTitle: '황건적의 난' } };
|
incomplete.payload = { publicAnnouncement: { enabled: true, scenarioTitle: '황건적의 난' } };
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ type LobbyFixtureOptions = {
|
|||||||
} | null;
|
} | null;
|
||||||
upcomingReset?: {
|
upcomingReset?: {
|
||||||
phase: 'SCHEDULED' | 'PREPARING' | 'READY' | 'DELAYED';
|
phase: 'SCHEDULED' | 'PREPARING' | 'READY' | 'DELAYED';
|
||||||
scheduledAt: string;
|
scheduledAt: string | null;
|
||||||
preopenAt: string;
|
preopenAt: string;
|
||||||
openAt: string;
|
openAt: string;
|
||||||
scenarioId: number;
|
scenarioId: number;
|
||||||
@@ -640,12 +640,12 @@ test('matches the normal preopen copy text and labels an immediate announcement
|
|||||||
await page.screenshot({ path: testInfo.outputPath('gateway-upcoming-reset-mobile.png'), fullPage: true });
|
await page.screenshot({ path: testInfo.outputPath('gateway-upcoming-reset-mobile.png'), fullPage: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('keeps the announcement through the RESERVED handoff after the build completes', async ({ page }) => {
|
test('shows a build-complete RESERVED announcement without the early-publication badge', async ({ page }) => {
|
||||||
const gameOperations = await installFixture(page, {
|
const gameOperations = await installFixture(page, {
|
||||||
profileStatus: 'RESERVED',
|
profileStatus: 'RESERVED',
|
||||||
upcomingReset: {
|
upcomingReset: {
|
||||||
phase: 'READY',
|
phase: 'READY',
|
||||||
scheduledAt: '2026-08-27T05:00:00.000Z',
|
scheduledAt: null,
|
||||||
preopenAt: '2026-08-27T05:30:00.000Z',
|
preopenAt: '2026-08-27T05:30:00.000Z',
|
||||||
openAt: '2026-08-27T11:00:00.000Z',
|
openAt: '2026-08-27T11:00:00.000Z',
|
||||||
scenarioId: 1010,
|
scenarioId: 1010,
|
||||||
@@ -661,14 +661,10 @@ test('keeps the announcement through the RESERVED handoff after the build comple
|
|||||||
|
|
||||||
await page.goto('lobby');
|
await page.goto('lobby');
|
||||||
const row = page.locator('tbody tr').filter({ hasText: 'hwe섭' });
|
const row = page.locator('tbody tr').filter({ hasText: 'hwe섭' });
|
||||||
const badge = row.getByTestId('reserved-announcement-badge');
|
|
||||||
await expect(row.getByTestId('upcoming-reset-phase')).toHaveCount(0);
|
await expect(row.getByTestId('upcoming-reset-phase')).toHaveCount(0);
|
||||||
await expect(row.getByTestId('upcoming-reset-scenario-title')).toHaveText('【가상】황건적의 난');
|
await expect(row.getByTestId('upcoming-reset-scenario-title')).toHaveText('【가상】황건적의 난');
|
||||||
await badge.hover();
|
await expect(row.getByTestId('reserved-announcement-badge')).toHaveCount(0);
|
||||||
await expect(row.getByTestId('reserved-announcement-build-tooltip')).toHaveText(
|
await expect(row.getByTestId('reserved-announcement-build-tooltip')).toHaveCount(0);
|
||||||
'실제 빌드 시작 : 2026-08-27 14:00:00'
|
|
||||||
);
|
|
||||||
await expect(row.getByTestId('reserved-announcement-build-tooltip')).not.toContainText('오픈 준비 완료');
|
|
||||||
await expect(row).not.toContainText('준 비 중 · 접근 불가');
|
await expect(row).not.toContainText('준 비 중 · 접근 불가');
|
||||||
await expect(row.getByRole('button', { name: '입장' })).toHaveCount(0);
|
await expect(row.getByRole('button', { name: '입장' })).toHaveCount(0);
|
||||||
expect(gameOperations).toEqual([]);
|
expect(gameOperations).toEqual([]);
|
||||||
|
|||||||
Reference in New Issue
Block a user