fix(linting): fixes linting styling / UI issues for v13 (#10073)

* fix: fixes issue where custom ruleset modal would not inherit the active theme styles; collapse the lint panel toolbar by default

* chore: address copilot feedback

* test: update test

* fix: increase minSize

* test: update tests

* test: attempt to fix test again

* test: attempt to fix test again

(cherry picked from commit 271ac44def)
This commit is contained in:
Fares Osman
2026-06-12 14:41:35 -04:00
committed by Insomnia
parent e6f6f9a6e5
commit 8cdee0eebf
3 changed files with 49 additions and 25 deletions

View File

@@ -61,6 +61,13 @@ const USER_B_SESSION = {
* does not include `info.x-smoke-test-marker`, so once our custom ruleset is
* uploaded the rule defined in fixtures/files/custom.spectral.yaml will fire.
*/
async function expandLintPanel(page: Page) {
const lintButton = page.getByTestId('lint-panel-toggle');
await expect.soft(lintButton).toBeVisible({ timeout: 15_000 });
await lintButton.click();
await expect.soft(page.getByTestId('lint-panel')).toBeAttached({ timeout: 15_000 });
}
async function openPetStoreDesignDoc(page: Page) {
await page.getByRole('button', { name: 'Create document' }).click();
await page.getByRole('dialog').getByRole('button', { name: 'Create' }).click();
@@ -73,7 +80,9 @@ async function uploadRuleset(insomnia: InsomniaApp, page: Page) {
await insomnia.queueOpenDialogResponse([RULESET_FIXTURE]);
await page.getByLabel('Upload custom ruleset').click();
// Soft assert per ESLint rule; a failure here will surface downstream as well.
await expect.soft(page.getByRole('button', { name: 'View selected ruleset content' })).toBeVisible({ timeout: 10_000 });
await expect
.soft(page.getByRole('button', { name: 'View selected ruleset content' }))
.toBeVisible({ timeout: 10_000 });
}
async function removeRuleset(page: Page) {
@@ -112,7 +121,10 @@ async function createGitDesignDocument(insomnia: InsomniaApp, page: Page, projec
await page.getByRole('textbox', { name: 'Name', exact: true }).fill('Lint Test Spec');
await page.getByRole('textbox', { name: /File name/ }).fill('lint_test_spec');
await page.getByRole('button', { name: 'Create', exact: true }).click();
await page.getByRole('dialog').waitFor({ state: 'hidden', timeout: 10_000 }).catch(() => {});
await page
.getByRole('dialog')
.waitFor({ state: 'hidden', timeout: 10_000 })
.catch(() => {});
// Populate with Pet Store example so the lint panel renders (requires non-empty apiSpec.contents).
await page.getByText('Use example').click();
await page.getByText('Pet Store').click();
@@ -159,10 +171,16 @@ test.describe('Custom Spectral Lint Rules', () => {
await uploadRuleset(insomnia, insomnia.page);
// Our custom rule should now fire on Pet Store.
await expandLintPanel(insomnia.page);
await expect.soft(insomnia.page.getByText(new RegExp(RULESET_RULE_NAME))).toBeVisible({
timeout: 15_000,
});
// Capture error/warning counts before relaunch so we can assert persistence.
const lintToggle = insomnia.page.getByTestId('lint-panel-toggle');
await expect.soft(lintToggle).toBeVisible({ timeout: 15_000 });
const lintSummaryBefore = await lintToggle.textContent();
// Close the Electron process and relaunch it against the same data path.
// This exercises the full persistence boundary: NeDB on disk, main-process
// startup, renderer init, clientLoader.
@@ -175,9 +193,10 @@ test.describe('Custom Spectral Lint Rules', () => {
await expect
.soft(insomnia.page.getByRole('button', { name: 'View selected ruleset content' }))
.toBeVisible({ timeout: 15_000 });
await expect.soft(insomnia.page.getByText(new RegExp(RULESET_RULE_NAME))).toBeVisible({
timeout: 15_000,
});
// Assert same error/warning counts as before relaunch (ruleset persisted).
const lintToggleAfter = insomnia.page.getByTestId('lint-panel-toggle');
await expect.soft(lintToggleAfter).toHaveText(lintSummaryBefore ?? '', { timeout: 15_000 });
});
// ---------------------------------------------------------------------------
@@ -186,6 +205,7 @@ test.describe('Custom Spectral Lint Rules', () => {
test('remove custom ruleset reverts to default OAS, persists after app relaunch', async ({ insomnia }) => {
await openPetStoreDesignDoc(insomnia.page);
await uploadRuleset(insomnia, insomnia.page);
await expandLintPanel(insomnia.page);
await expect.soft(insomnia.page.getByText(new RegExp(RULESET_RULE_NAME))).toBeVisible({
timeout: 15_000,
});
@@ -204,7 +224,9 @@ test.describe('Custom Spectral Lint Rules', () => {
// ---------------------------------------------------------------------------
// 3. Invalid ruleset — error modal appears, ruleset is not applied
// ---------------------------------------------------------------------------
test('uploading a ruleset with disallowed keys shows an error and leaves default ruleset active', async ({ insomnia }) => {
test('uploading a ruleset with disallowed keys shows an error and leaves default ruleset active', async ({
insomnia,
}) => {
await openPetStoreDesignDoc(insomnia.page);
await expect.soft(insomnia.page.getByText('Default OAS Ruleset')).toBeVisible();
@@ -218,9 +240,7 @@ test.describe('Custom Spectral Lint Rules', () => {
// Default ruleset should still be active; no custom upload button state change.
await expect.soft(insomnia.page.getByText('Default OAS Ruleset')).toBeVisible();
await expect
.soft(insomnia.page.getByRole('button', { name: 'View selected ruleset content' }))
.toBeHidden();
await expect.soft(insomnia.page.getByRole('button', { name: 'View selected ruleset content' })).toBeHidden();
});
// ---------------------------------------------------------------------------
@@ -293,9 +313,8 @@ test.describe('Custom Spectral Lint Rules', () => {
await expect
.soft(machineB.page.getByRole('button', { name: 'View selected ruleset content' }))
.toBeVisible({ timeout: 15_000 });
await expect
.soft(machineB.page.getByText(new RegExp(RULESET_RULE_NAME)))
.toBeVisible({ timeout: 15_000 });
await expandLintPanel(machineB.page);
await expect.soft(machineB.page.getByText(new RegExp(RULESET_RULE_NAME))).toBeVisible({ timeout: 15_000 });
await fetch(`${devServerUrl}/__test-config/cloud-sync/reset`, { method: 'POST' });
});
@@ -334,9 +353,8 @@ test.describe('Custom Spectral Lint Rules', () => {
await expect
.soft(userB.page.getByRole('button', { name: 'View selected ruleset content' }))
.toBeVisible({ timeout: 15_000 });
await expect
.soft(userB.page.getByText(new RegExp(RULESET_RULE_NAME)))
.toBeVisible({ timeout: 15_000 });
await expandLintPanel(userB.page);
await expect.soft(userB.page.getByText(new RegExp(RULESET_RULE_NAME))).toBeVisible({ timeout: 15_000 });
await fetch(`${devServerUrl}/__test-config/cloud-sync/reset`, { method: 'POST' });
});

View File

@@ -19,7 +19,9 @@ test('can render Spectral OpenAPI lint errors', async ({ page }) => {
const lintSummary = page.getByRole('button', { name: /error/ });
await expect.soft(lintSummary).toBeVisible();
// Expand the lint error message group & assert line number occurence
// Expand the lint panel
await page.getByTestId('lint-panel-toggle').click();
await expect.soft(page.getByTestId('lint-panel')).toBeVisible();
const lintEntry = page.getByText(/oas3-schema/);
await expect.soft(lintEntry).toBeVisible();
await lintEntry.click();

View File

@@ -660,7 +660,7 @@ const Component = ({ params }: Route.ComponentProps) => {
const lintToolbar = (
<div
className={`flex flex-wrap items-center gap-2 border-solid border-(--hl-md) p-(--padding-sm) ${isLintPaneOpen ? 'border-b' : ''}`}
className={`flex h-(--line-height-sm) items-center gap-2 overflow-hidden border-solid border-(--hl-md) px-(--padding-sm) ${isLintPaneOpen ? 'border-b' : ''}`}
>
<div className="inline-flex items-center gap-2">
<Icon icon={selectedRulesetPath ? 'file-circle-check' : 'file-circle-xmark'} />
@@ -774,6 +774,7 @@ const Component = ({ params }: Route.ComponentProps) => {
'No lint problems'
) : (
<Button
data-testid="lint-panel-toggle"
onPress={() => (isLintPaneOpen ? lintPanelRef.current?.collapse() : lintPanelRef.current?.expand())}
>
<span className="underline">
@@ -881,6 +882,7 @@ const Component = ({ params }: Route.ComponentProps) => {
)}
<ToggleButton
aria-label="Toggle preview"
data-testid="preview-toggle"
isSelected={isSpecPaneOpen}
className="flex h-full items-center justify-center gap-2 rounded-xs px-2 text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
onChange={value => {
@@ -1306,9 +1308,9 @@ const Component = ({ params }: Route.ComponentProps) => {
isOpen={isViewRulesetModalOpen}
onOpenChange={setIsViewRulesetModalOpen}
isDismissable
className="theme--transparent-overlay fixed top-0 left-0 z-10 flex h-(--visual-viewport-height) w-full justify-center bg-(--color-bg) py-[100px]"
className="fixed top-0 left-0 z-10 flex h-(--visual-viewport-height) w-full items-center justify-center bg-black/30"
>
<Modal className="theme--dialog h-fit max-h-full w-full max-w-[900px] overflow-y-auto rounded-md border border-solid border-(--hl-sm) bg-(--color-bg) p-[32px] text-(--color-font)">
<Modal className="max-h-full w-full max-w-[900px] overflow-y-auto rounded-md border border-solid border-(--hl-sm) bg-(--color-bg) p-(--padding-lg) text-(--color-font)">
<Dialog className="relative outline-hidden">
{({ close }) => (
<>
@@ -1355,17 +1357,19 @@ const Component = ({ params }: Route.ComponentProps) => {
<Panel
ref={lintPanelRef}
id="lint-panel"
defaultSize={20}
minSize={5}
defaultSize={0}
minSize={10}
collapsible
onCollapse={() => setIsLintPaneOpen(false)}
onExpand={() => setIsLintPaneOpen(true)}
className="flex flex-col overflow-hidden"
>
<div className="box-border flex h-full flex-col">
{lintToolbar}
{lintMessageList}
</div>
{isLintPaneOpen && (
<div data-testid="lint-panel" className="box-border flex h-full flex-col">
{lintToolbar}
{lintMessageList}
</div>
)}
</Panel>
</>
)}