Skip to content
51 changes: 13 additions & 38 deletions frontend/tests/AddCardToFavorites.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,17 @@ import {

import { test } from "../playwright.setup";
import {
expectCardGridSlotState,
closeDetailedView,
importTextOnEditorLanding,
loadPageWithDefaultBackend,
openDetailedView,
} from "./test-utils";

// Proposal H switchover (2026-07-23, issues #231/#272) - /editor now serves the unified
// sheet+rail page (`DisplayPage.tsx`); the classic grid `ProjectEditor` this file's own setup
// depends on (via testids/interaction patterns like `front-slot`/`back-slot`/`common-cardback`/
// the "Add Cards" right-panel dropdown/the classic "Print!" tab, or a component with no rendered
// equivalent on the new page yet - see issue #272's own tracked parity gaps) is fully unrouted,
// not just delisted from the nav. Skipped here rather than deleted (component files themselves
// are untouched, per this swap's own scope) or silently left red - porting this coverage to
// DisplayPage's DOM is real, non-mechanical work tracked against #272, not done as part of the
// route swap itself (the owner's directive was to proceed with the swap regardless of the
// checklist's open items).
test.beforeEach(async ({}, testInfo) => {
testInfo.skip(
true,
"Proposal H switchover (2026-07-23): tests classic /editor-only UI, now unrouted - see issue #272"
);
});

const openDetailedView = async (page: any, cardName: string) => {
await page.getByAltText(cardName).click();
await expect(page.getByText("Card Details")).toBeVisible();
};

const closeDetailedView = async (page: any) => {
await page.getByTestId("detailed-view").getByLabel("Close").click();
await expect(page.getByText("Card Details")).not.toBeVisible();
};
// Proposal H parity port (2026-07-23, issue #272 wave 1): ported onto the unified /editor page -
// AddCardToFavorites (via CardDownloadFavorite) lives inside CardDetailedViewModal's own "Card
// Details" region, reached the same way the rest of this cluster reaches the modal - see
// openDetailedView's own module comment (test-utils.ts) for the Browse-mode route and the "Card
// details" text-collision fix.

test.describe("AddCardToFavorites tests", () => {
test("renders Add to Favorites button when card is not a favorite", async ({
Expand All @@ -62,9 +42,8 @@ test.describe("AddCardToFavorites tests", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await expectCardGridSlotState(page, 1, "front", cardDocument1.name, 1, 1);

await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

const button = page.getByRole("button", { name: /Add to Favorites/i });
await expect(button).toBeVisible();
Expand All @@ -85,9 +64,8 @@ test.describe("AddCardToFavorites tests", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await expectCardGridSlotState(page, 1, "front", cardDocument1.name, 1, 1);

await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

// Initially shows "Add to Favorites"
const button = page.getByRole("button", { name: /Add to Favorites/i });
Expand Down Expand Up @@ -127,9 +105,8 @@ test.describe("AddCardToFavorites tests", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await expectCardGridSlotState(page, 1, "front", cardDocument1.name, 1, 1);

await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

// First add to favorites
const addButton = page.getByRole("button", { name: /Add to Favorites/i });
Expand Down Expand Up @@ -175,9 +152,8 @@ test.describe("AddCardToFavorites tests", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await expectCardGridSlotState(page, 1, "front", cardDocument1.name, 1, 1);

await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

// First click: Add to favorites
await page.getByRole("button", { name: /Add to Favorites/i }).click();
Expand Down Expand Up @@ -215,10 +191,9 @@ test.describe("AddCardToFavorites tests", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await expectCardGridSlotState(page, 1, "front", cardDocument1.name, 1, 1);

// Open detailed view and add to favorites
await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);
await page.getByRole("button", { name: /Add to Favorites/i }).click();
await expect(
page.getByRole("button", { name: /Remove from Favorites/i })
Expand All @@ -228,7 +203,7 @@ test.describe("AddCardToFavorites tests", () => {
await closeDetailedView(page);

// Reopen the detailed view
await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

// Should still show as a favorite
await expect(
Expand Down
105 changes: 35 additions & 70 deletions frontend/tests/AddCardToProjectForm.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,19 @@ import {

import { test } from "../playwright.setup";
import {
expectCardGridSlotStates,
closeDetailedView,
expectDisplaySheetSlotStates,
importTextOnEditorLanding,
loadPageWithDefaultBackend,
openDetailedView,
} from "./test-utils";

// Proposal H switchover (2026-07-23, issues #231/#272) - /editor now serves the unified
// sheet+rail page (`DisplayPage.tsx`); the classic grid `ProjectEditor` this file's own setup
// depends on (via testids/interaction patterns like `front-slot`/`back-slot`/`common-cardback`/
// the "Add Cards" right-panel dropdown/the classic "Print!" tab, or a component with no rendered
// equivalent on the new page yet - see issue #272's own tracked parity gaps) is fully unrouted,
// not just delisted from the nav. Skipped here rather than deleted (component files themselves
// are untouched, per this swap's own scope) or silently left red - porting this coverage to
// DisplayPage's DOM is real, non-mechanical work tracked against #272, not done as part of the
// route swap itself (the owner's directive was to proceed with the swap regardless of the
// checklist's open items).
test.beforeEach(async ({}, testInfo) => {
testInfo.skip(
true,
"Proposal H switchover (2026-07-23): tests classic /editor-only UI, now unrouted - see issue #272"
);
});
// Proposal H parity port (2026-07-23, issue #272 wave 1): ported onto the unified /editor page -
// AddCardToProjectForm is unforked and mounted the same way the rest of the card-detail modal
// cluster reaches it (Browse mode - see openDetailedView's own module comment, test-utils.ts).
// Per-slot assertions ported via expectDisplaySheetSlotStates - see that helper's own comment for
// why dropping the selectedImage/totalImages numeric checks doesn't weaken what this test actually
// verifies (the right card landing in the right slot, the right number of times).

test.describe("AddCardToProjectForm tests", () => {
for (const quantity of [1, 2, 3]) {
Expand All @@ -55,77 +47,50 @@ test.describe("AddCardToProjectForm tests", () => {
`card one${SelectedImageSeparator}${cardDocument1.identifier}`
);

await expectCardGridSlotStates(
await expectDisplaySheetSlotStates(
page,
[
{
slot: 1,
name: cardDocument1.name,
selectedImage: 1,
totalImages: 1,
},
],
[
{
slot: 1,
name: cardDocument2.name,
selectedImage: 1,
totalImages: 2,
},
]
[{ slot: 1, name: cardDocument1.name }],
[{ slot: 1, name: cardDocument2.name }]
);

// Click on the card to open detailed view
await page.getByAltText(cardDocument1.name).click();
await expect(page.getByText("Card Details")).toBeVisible();
// Open the detail view (via Browse mode) for the same card already in the project
await openDetailedView(page, "card one", cardDocument1.identifier);

// Fill in the quantity
const quantityInput = page.getByAltText(
// Fill in the quantity - scoped to the modal itself, since Browse mode's own catalog tile
// (still in the DOM behind the modal) mounts its own, unforked AddCardToProjectForm too
// (CatalogBrowseResults.tsx's own "+Add" affordance) - a bare page-wide locator collides
// with both.
const detailedView = page.getByTestId("detailed-view");
const quantityInput = detailedView.getByAltText(
"Quantity of card to add to project"
);
await quantityInput.clear();
await quantityInput.fill(quantity.toString());

// Add to project
await page.getByRole("button", { name: "Add to Project" }).click();
await detailedView
.getByRole("button", { name: "Add to Project" })
.click();

// Close the detailed view
await page.getByTestId("detailed-view").getByLabel("Close").click();
await closeDetailedView(page);

// openDetailedView's own Browse-mode route (test-utils.ts) left the shared Add/Browse
// toggle on Browse - the center region renders catalog results, not the print-sheet stack,
// while that's active (DisplayPage.tsx's own module comment), so the sheet assertions below
// need Add mode back first.
await page.getByTestId("display-search-mode-add").click();

// Verify that the cards were added (original slot + new slots)
const expectedFronts = [
{
slot: 1,
name: cardDocument1.name,
selectedImage: 1,
totalImages: 1,
},
];
const expectedBacks = [
{
slot: 1,
name: cardDocument2.name,
selectedImage: 1,
totalImages: 2,
},
];
const expectedFronts = [{ slot: 1, name: cardDocument1.name }];
const expectedBacks = [{ slot: 1, name: cardDocument2.name }];

for (let i = 0; i < quantity; i++) {
expectedFronts.push({
slot: i + 2,
name: cardDocument1.name,
selectedImage: 1,
totalImages: 1,
});
expectedBacks.push({
slot: i + 2,
name: cardDocument2.name,
selectedImage: 1,
totalImages: 2,
});
expectedFronts.push({ slot: i + 2, name: cardDocument1.name });
expectedBacks.push({ slot: i + 2, name: cardDocument2.name });
}

await expectCardGridSlotStates(page, expectedFronts, expectedBacks);
await expectDisplaySheetSlotStates(page, expectedFronts, expectedBacks);
});
}
});
35 changes: 9 additions & 26 deletions frontend/tests/ArtistSupportLink.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,15 @@ import { test } from "../playwright.setup";
import {
importTextOnEditorLanding,
loadPageWithDefaultBackend,
openDetailedView,
} from "./test-utils";

// Proposal H switchover (2026-07-23, issues #231/#272) - /editor now serves the unified
// sheet+rail page (`DisplayPage.tsx`); the classic grid `ProjectEditor` this file's own setup
// depends on (via testids/interaction patterns like `front-slot`/`back-slot`/`common-cardback`/
// the "Add Cards" right-panel dropdown/the classic "Print!" tab, or a component with no rendered
// equivalent on the new page yet - see issue #272's own tracked parity gaps) is fully unrouted,
// not just delisted from the nav. Skipped here rather than deleted (component files themselves
// are untouched, per this swap's own scope) or silently left red - porting this coverage to
// DisplayPage's DOM is real, non-mechanical work tracked against #272, not done as part of the
// route swap itself (the owner's directive was to proceed with the swap regardless of the
// checklist's open items).
test.beforeEach(async ({}, testInfo) => {
testInfo.skip(
true,
"Proposal H switchover (2026-07-23): tests classic /editor-only UI, now unrouted - see issue #272"
);
});

const openDetailedView = async (page: any, cardName: string) => {
await page.getByAltText(cardName).click();
await expect(page.getByText("Card Details")).toBeVisible();
};

// Artist Support Links v1, surface 1: the Card Detail Modal's "Canonical Aritst" row (see
// docs/features/artist-support-links.md). Surface 2 (the /whatsthat post-answer moment) is
// Proposal H parity port (2026-07-23, issue #272 wave 1): ported onto the unified /editor page.
// CardDetailedViewModal (the shared, unforked component this whole cluster exercises) is reached
// via Browse mode - see openDetailedView's own module comment (test-utils.ts) for why that's the
// one surface on this page that still opens it, and the "Card details" text-collision fix that
// cluster needed. Its own "Canonical Aritst" table row is what this file's own assertions target
// (docs/features/artist-support-links.md). Surface 2 (the /whatsthat post-answer moment) is
// covered in QuestionFeed.spec.ts.
test.describe("Artist Support Link - Card Detail Modal", () => {
test("a known canonical artist renders as an Artist Support Link, built deterministically from their name", async ({
Expand All @@ -64,7 +47,7 @@ test.describe("Artist Support Link - Card Detail Modal", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

const link = page.getByTestId("artist-support-link");
await expect(link).toBeVisible();
Expand Down Expand Up @@ -98,7 +81,7 @@ test.describe("Artist Support Link - Card Detail Modal", () => {
page,
`my search query${SelectedImageSeparator}${cardDocument1.identifier}`
);
await openDetailedView(page, cardDocument1.name);
await openDetailedView(page, "my search query", cardDocument1.identifier);

await expect(page.getByTestId("artist-support-link")).toHaveCount(0);
await expect(
Expand Down
Loading