mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
feat: add an inline deploy-progress style for the stack detail (#1355)
* feat: add an inline deploy-progress style for the stack detail Deploy progress gains a presentation choice under Settings > Appearance > Display: Modal (the default centered overlay) or Inline. In Inline style a compact status band on the stack detail shows the running operation, its elapsed time, the live phase, the latest output line, and the post-update health gate result. A "View output" button opens the full log modal on demand, a dismiss control clears the band, and the band auto-clears a few seconds after a clean completion. The live progress socket is lifted to an always-mounted owner so the band streams without the modal; the default Modal style is unchanged. Operations carry their node so a band never bleeds onto a same-named stack on another node. The stack detail's redundant "CONTAINERS" section heading is removed; the band reserves that vertical space. * fix: keep inline deploy progress reachable off the stack detail Review of the inline presentation found a gap: a failed operation, an App Store install, or navigating away leaves the inline session with no visible surface, since the band only renders on the operation's own stack detail. Restore the minimized pill as the inline fallback, shown only when the band is not covering the session, so there is always a click-through to the log without ever overlapping the band. Closing the modal for a failed op now ends the session (the band has stepped aside) instead of only hiding it. Also document the unsupported mid-operation style switch, and refresh the deploy-progress, settings, appearance, and app-store docs for the renamed "Deploy progress" setting and the Modal/Inline choice.
This commit is contained in:
@@ -14,6 +14,7 @@ import { loginAs, waitForStacksLoaded } from './helpers';
|
||||
const HAPPY_STACK = 'e2e-deploy-log-test';
|
||||
const FAIL_STACK = 'e2e-deploy-log-fail-test';
|
||||
const DEPLOY_FEEDBACK_KEY = 'sencho.deploy-feedback.enabled';
|
||||
const DEPLOY_FEEDBACK_STYLE_KEY = 'sencho.deploy-feedback.style';
|
||||
|
||||
const HAPPY_COMPOSE = `services:
|
||||
web:
|
||||
@@ -77,6 +78,21 @@ async function enableDeployFeedback(page: Page): Promise<void> {
|
||||
}, DEPLOY_FEEDBACK_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the progress presentation style (Modal or Inline). addInitScript keeps
|
||||
* it set across the reload setupDeployStack performs; the evaluate updates any
|
||||
* already-mounted tree.
|
||||
*/
|
||||
async function setDeployStyle(page: Page, style: 'modal' | 'inline'): Promise<void> {
|
||||
await page.addInitScript(([key, value]) => {
|
||||
window.localStorage.setItem(key, value);
|
||||
}, [DEPLOY_FEEDBACK_STYLE_KEY, style] as const);
|
||||
await page.evaluate(([key, value]) => {
|
||||
window.localStorage.setItem(key, value);
|
||||
window.dispatchEvent(new CustomEvent('sencho-settings-changed'));
|
||||
}, [DEPLOY_FEEDBACK_STYLE_KEY, style] as const);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrink the post-deploy health gate observation window so success-path tests
|
||||
* can watch the full deploy, verify, succeed sequence without waiting out the
|
||||
@@ -384,3 +400,72 @@ test.describe('Deploy feedback modal', () => {
|
||||
await setHealthGateWindow(page, 90).catch(() => {});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Inline deploy progress', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await loginAs(page);
|
||||
await waitForStacksLoaded(page);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await deleteStackViaApi(page, HAPPY_STACK);
|
||||
await disableDeployFeedback(page);
|
||||
await page.evaluate((key) => window.localStorage.removeItem(key), DEPLOY_FEEDBACK_STYLE_KEY).catch(() => {});
|
||||
await setHealthGateWindow(page, 90).catch(() => {});
|
||||
});
|
||||
|
||||
test('inline style shows the in-page banner instead of the modal, and View output opens it', async ({ page }) => {
|
||||
test.setTimeout(120_000);
|
||||
|
||||
await enableDeployFeedback(page);
|
||||
await setDeployStyle(page, 'inline');
|
||||
await setHealthGateWindow(page, 15);
|
||||
await setupDeployStack(page, HAPPY_STACK, HAPPY_COMPOSE);
|
||||
await syncDeployFeedbackState(page);
|
||||
|
||||
await page.getByTestId('stack-deploy-button').click();
|
||||
|
||||
const banner = page.getByTestId('stack-operation-banner');
|
||||
await expect(banner).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// The modal must not auto-open in Inline style.
|
||||
await expect(page.locator('[data-testid="deploy-feedback-modal"]')).toBeHidden();
|
||||
|
||||
// View output summons the full modal on demand.
|
||||
await banner.getByRole('button', { name: /view output/i }).click();
|
||||
await expect(page.locator('[data-testid="deploy-feedback-modal"]')).toBeVisible({ timeout: 5_000 });
|
||||
});
|
||||
|
||||
test('inline banner is visible on the mobile detail', async ({ page }) => {
|
||||
test.setTimeout(120_000);
|
||||
|
||||
await enableDeployFeedback(page);
|
||||
await setDeployStyle(page, 'inline');
|
||||
await setHealthGateWindow(page, 15);
|
||||
await setupDeployStack(page, HAPPY_STACK, HAPPY_COMPOSE);
|
||||
await syncDeployFeedbackState(page);
|
||||
|
||||
// Resize below md so the detail re-renders as the mobile segmented layout
|
||||
// with the same selected stack.
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
|
||||
await page.getByTestId('stack-deploy-button').click();
|
||||
await expect(page.getByTestId('stack-operation-banner')).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('modal style shows no inline banner', async ({ page }) => {
|
||||
test.setTimeout(120_000);
|
||||
|
||||
await enableDeployFeedback(page);
|
||||
await setDeployStyle(page, 'modal');
|
||||
await setHealthGateWindow(page, 15);
|
||||
await setupDeployStack(page, HAPPY_STACK, HAPPY_COMPOSE);
|
||||
await syncDeployFeedbackState(page);
|
||||
|
||||
await page.getByTestId('stack-deploy-button').click();
|
||||
await page.mouse.move(0, 0);
|
||||
|
||||
await expect(page.locator('[data-testid="deploy-feedback-modal"]')).toBeVisible({ timeout: 10_000 });
|
||||
await expect(page.getByTestId('stack-operation-banner')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user