feat(FN-249): fix 15 failing regression tests — FeatureMatrix duplicate selectors
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged: - fix(FN-249): fix 15 failing regression tests — FeatureMatrix duplicate selectors Files changed: .../subscription/__tests__/index.test.tsx | 145 +++++++++++---------- 1 file changed, 77 insertions(+), 68 deletions(-) Fusion-Task-Id: FN-249
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
* Tests cover rendering, interaction, trust/i18n, edge cases, and accessibility.
|
||||
*/
|
||||
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// ─── Pre-declare mock variables via vi.hoisted() ─────────────────────────────
|
||||
@@ -277,21 +277,18 @@ describe("rendering", () => {
|
||||
describe("plan cards", () => {
|
||||
it("renders all 4 plan cards with correct names and monthly prices", () => {
|
||||
renderPage();
|
||||
// Use getAllByText — FeatureMatrix table also renders plan names in <th> headers
|
||||
expect(screen.getAllByText("subscription.plans.brand1.name").length).toBeGreaterThanOrEqual(
|
||||
2,
|
||||
);
|
||||
expect(screen.getAllByText("subscription.plans.brand2.name").length).toBeGreaterThanOrEqual(
|
||||
2,
|
||||
);
|
||||
expect(screen.getAllByText("subscription.plans.brand3.name").length).toBeGreaterThanOrEqual(
|
||||
2,
|
||||
);
|
||||
expect(screen.getAllByText("subscription.plans.full.name").length).toBeGreaterThanOrEqual(2);
|
||||
expect(screen.getByText("₺200,00")).toBeInTheDocument();
|
||||
expect(screen.getByText("₺350,00")).toBeInTheDocument();
|
||||
expect(screen.getByText("₺500,00")).toBeInTheDocument();
|
||||
expect(screen.getByText("₺999,00")).toBeInTheDocument();
|
||||
// Scope to plan comparison grid to avoid FeatureMatrix table duplicates
|
||||
const planGrid = document.querySelector(".grid.gap-4");
|
||||
expect(planGrid).toBeInTheDocument();
|
||||
const gridScope = within(planGrid as HTMLElement);
|
||||
expect(gridScope.getByText("subscription.plans.brand1.name")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("subscription.plans.brand2.name")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("subscription.plans.brand3.name")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("subscription.plans.full.name")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("₺200,00")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("₺350,00")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("₺500,00")).toBeInTheDocument();
|
||||
expect(gridScope.getByText("₺999,00")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders feature lists for each plan", () => {
|
||||
@@ -340,23 +337,23 @@ describe("rendering", () => {
|
||||
describe("popular plan visual dominance (P0-2)", () => {
|
||||
it("brand2 (popular) card has stronger visual styling", () => {
|
||||
renderPage();
|
||||
// FeatureMatrix also has a popular badge, so pick the one on the plan card (not inside <th>)
|
||||
const allPopular = screen.getAllByText("subscription.popular");
|
||||
const popularBadge = allPopular.find((el) => !el.closest("th"))!;
|
||||
expect(popularBadge).toBeInTheDocument();
|
||||
// Two "subscription.popular" elements: one in plan card, one in FeatureMatrix
|
||||
const popularBadges = screen.getAllByText("subscription.popular");
|
||||
expect(popularBadges.length).toBe(2);
|
||||
// Card badge has text-xs (not text-[10px] like FeatureMatrix)
|
||||
const cardBadge = popularBadges.find(
|
||||
(el) => el.className.includes("text-xs") && !el.className.includes("text-[10px]"),
|
||||
)!;
|
||||
// The popular badge is inside a <Badge> element; walk up to the Card
|
||||
const popularCard = popularBadge.closest('[class*="rounded-xl"]');
|
||||
const popularCard = cardBadge.closest('[class*="rounded-xl"]');
|
||||
expect(popularCard).toBeTruthy();
|
||||
expect(popularCard?.className).toContain("shadow-xl");
|
||||
});
|
||||
|
||||
it("only one plan card has popular badge", () => {
|
||||
it("only one plan card has popular badge (2 total with FeatureMatrix)", () => {
|
||||
renderPage();
|
||||
// FeatureMatrix also has a popular badge; count only card badges (not in <th>)
|
||||
const cardPopularBadges = screen
|
||||
.getAllByText("subscription.popular")
|
||||
.filter((el) => !el.closest("th"));
|
||||
expect(cardPopularBadges.length).toBe(1);
|
||||
// 2 total: plan card + FeatureMatrix table header
|
||||
expect(screen.getAllByText("subscription.popular").length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -419,10 +416,11 @@ describe("interaction", () => {
|
||||
it("CTA says Devam Et (proceed) when plan selected", () => {
|
||||
renderPage();
|
||||
expect(screen.queryByText("subscription.proceed")).not.toBeInTheDocument();
|
||||
// FeatureMatrix <th> also matches — pick the plan card element (not inside <th>)
|
||||
const brand1Card = screen
|
||||
.getAllByText("subscription.plans.brand1.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
// Scope to plan comparison grid (not FeatureMatrix table)
|
||||
const planGrid = document.querySelector(".grid.gap-4");
|
||||
const gridScope = within(planGrid as HTMLElement);
|
||||
const brand1Card = gridScope
|
||||
.getByText("subscription.plans.brand1.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand1Card);
|
||||
const ctaElements = screen.getAllByText(/subscription\.proceed/);
|
||||
@@ -431,9 +429,10 @@ describe("interaction", () => {
|
||||
|
||||
it("CTA includes price info with ile Devam Et format", () => {
|
||||
renderPage();
|
||||
const brand2Card = screen
|
||||
.getAllByText("subscription.plans.brand2.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGrid = document.querySelector(".grid.gap-4");
|
||||
const gridScope = within(planGrid as HTMLElement);
|
||||
const brand2Card = gridScope
|
||||
.getByText("subscription.plans.brand2.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand2Card);
|
||||
const ctaButtons = screen.getAllByRole("button", { name: /subscription\.proceed/ });
|
||||
@@ -451,16 +450,17 @@ describe("interaction", () => {
|
||||
|
||||
it("order summary renders with plan, period, and price when plan selected", () => {
|
||||
renderPage();
|
||||
const brand3Card = screen
|
||||
.getAllByText("subscription.plans.brand3.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGrid = document.querySelector(".grid.gap-4");
|
||||
const gridScope = within(planGrid as HTMLElement);
|
||||
const brand3Card = gridScope
|
||||
.getByText("subscription.plans.brand3.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand3Card);
|
||||
expect(screen.getByText("subscription.orderSummary")).toBeInTheDocument();
|
||||
expect(screen.getByText("subscription.orderSummaryPlan")).toBeInTheDocument();
|
||||
expect(screen.getByText("subscription.orderSummaryPeriod")).toBeInTheDocument();
|
||||
expect(screen.getByText("subscription.orderSummaryPrice")).toBeInTheDocument();
|
||||
// Plan name appears in both card and summary — use getAllByText
|
||||
// Plan name appears in card, summary, and FeatureMatrix — use getAllByText
|
||||
expect(screen.getAllByText("subscription.plans.brand3.name").length).toBeGreaterThanOrEqual(
|
||||
1,
|
||||
);
|
||||
@@ -470,9 +470,10 @@ describe("interaction", () => {
|
||||
|
||||
it("order summary updates when billing period changes", () => {
|
||||
renderPage();
|
||||
const brand1Card = screen
|
||||
.getAllByText("subscription.plans.brand1.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGrid1 = document.querySelector(".grid.gap-4");
|
||||
const gridScope1 = within(planGrid1 as HTMLElement);
|
||||
const brand1Card = gridScope1
|
||||
.getByText("subscription.plans.brand1.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand1Card);
|
||||
fireEvent.click(screen.getByRole("button", { name: "common.yearly" }));
|
||||
@@ -508,9 +509,10 @@ describe("interaction", () => {
|
||||
describe("proceed to payment navigation", () => {
|
||||
it("navigates to /dashboard/subscription/pay with correct search params", () => {
|
||||
renderPage();
|
||||
const brand2Card = screen
|
||||
.getAllByText("subscription.plans.brand2.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGridNav = document.querySelector(".grid.gap-4");
|
||||
const gridScopeNav = within(planGridNav as HTMLElement);
|
||||
const brand2Card = gridScopeNav
|
||||
.getByText("subscription.plans.brand2.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand2Card);
|
||||
fireEvent.click(screen.getByTestId("brand-selector-select"));
|
||||
@@ -530,9 +532,10 @@ describe("interaction", () => {
|
||||
|
||||
it("shows error toast when proceeding without brand selection", () => {
|
||||
renderPage();
|
||||
const brand1Card = screen
|
||||
.getAllByText("subscription.plans.brand1.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGridErr = document.querySelector(".grid.gap-4");
|
||||
const gridScopeErr = within(planGridErr as HTMLElement);
|
||||
const brand1Card = gridScopeErr
|
||||
.getByText("subscription.plans.brand1.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand1Card);
|
||||
const ctaButtons = screen.getAllByRole("button", { name: /subscription\.proceed/ });
|
||||
@@ -556,9 +559,10 @@ describe("interaction", () => {
|
||||
},
|
||||
eligibleForTrial: false,
|
||||
});
|
||||
const brand1Card = screen
|
||||
.getAllByText("subscription.plans.brand1.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGridCur = document.querySelector(".grid.gap-4");
|
||||
const gridScopeCur = within(planGridCur as HTMLElement);
|
||||
const brand1Card = gridScopeCur
|
||||
.getByText("subscription.plans.brand1.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
expect(brand1Card.className).toContain("cursor-pointer");
|
||||
// Current plan card should NOT be clickable
|
||||
@@ -579,9 +583,10 @@ describe("interaction", () => {
|
||||
describe("plan selection persists across billing toggle", () => {
|
||||
it("selected plan stays selected after switching billing period", () => {
|
||||
renderPage();
|
||||
const brand3Card = screen
|
||||
.getAllByText("subscription.plans.brand3.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGridPersist = document.querySelector(".grid.gap-4");
|
||||
const gridScopePersist = within(planGridPersist as HTMLElement);
|
||||
const brand3Card = gridScopePersist
|
||||
.getByText("subscription.plans.brand3.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand3Card);
|
||||
expect(screen.getByText("subscription.orderSummary")).toBeInTheDocument();
|
||||
@@ -599,9 +604,10 @@ describe("trust and i18n", () => {
|
||||
describe("PostHog events (P0-10)", () => {
|
||||
it("fires checkout_started event when proceeding to payment", () => {
|
||||
renderPage();
|
||||
const brand2Card = screen
|
||||
.getAllByText("subscription.plans.brand2.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGridPH1 = document.querySelector(".grid.gap-4");
|
||||
const gridScopePH1 = within(planGridPH1 as HTMLElement);
|
||||
const brand2Card = gridScopePH1
|
||||
.getByText("subscription.plans.brand2.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand2Card);
|
||||
fireEvent.click(screen.getByTestId("brand-selector-select"));
|
||||
@@ -615,9 +621,10 @@ describe("trust and i18n", () => {
|
||||
|
||||
it("fires plan_selected event when clicking a plan card", () => {
|
||||
renderPage();
|
||||
const brand3Card = screen
|
||||
.getAllByText("subscription.plans.brand3.name")
|
||||
.find((el) => !el.closest("th"))!
|
||||
const planGridPH2 = document.querySelector(".grid.gap-4");
|
||||
const gridScopePH2 = within(planGridPH2 as HTMLElement);
|
||||
const brand3Card = gridScopePH2
|
||||
.getByText("subscription.plans.brand3.name")
|
||||
.closest('[class*="relative"]')!;
|
||||
fireEvent.click(brand3Card);
|
||||
expect(mockCapture).toHaveBeenCalledWith("plan_selected", { plan: "brand3" });
|
||||
@@ -834,21 +841,23 @@ describe("accessibility", () => {
|
||||
|
||||
it("plan cards have visible names", () => {
|
||||
renderPage();
|
||||
const planGridAcc = document.querySelector(".grid.gap-4");
|
||||
const gridScopeAcc = within(planGridAcc as HTMLElement);
|
||||
["brand1", "brand2", "brand3", "full"].forEach((key) => {
|
||||
// FeatureMatrix <th> also matches — verify at least one match exists (plan card)
|
||||
const matches = screen.getAllByText(`subscription.plans.${key}.name`);
|
||||
expect(matches.length).toBeGreaterThanOrEqual(2);
|
||||
expect(gridScopeAcc.getByText(`subscription.plans.${key}.name`)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("popular plan is distinguishable beyond color", () => {
|
||||
renderPage();
|
||||
// FeatureMatrix also has a popular badge — pick the one on the plan card
|
||||
const popularBadge = screen
|
||||
.getAllByText("subscription.popular")
|
||||
.find((el) => !el.closest("th"))!;
|
||||
expect(popularBadge).toBeInTheDocument();
|
||||
const popularCard = popularBadge.closest('[class*="ring-2"]');
|
||||
// Two "subscription.popular" elements: plan card badge + FeatureMatrix badge
|
||||
const popularBadges = screen.getAllByText("subscription.popular");
|
||||
expect(popularBadges.length).toBeGreaterThanOrEqual(1);
|
||||
// Card badge has text-xs (not text-[10px] like FeatureMatrix)
|
||||
const cardBadge = popularBadges.find(
|
||||
(el) => el.className.includes("text-xs") && !el.className.includes("text-[10px]"),
|
||||
)!;
|
||||
const popularCard = cardBadge.closest('[class*="ring-2"]');
|
||||
expect(popularCard).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user