Skip to content

Commit d788832

Browse files
yyodsclaude
andcommitted
[red] pin five-target home and support copy to the selected release
The front page must present the Raspberry Pi Pico 2 W and ESP32-C3 consistently with the flash page's five-profile reality. Pin: the home availability grid names all five v0.6.0 targets — Raspberry Pi Pico 2 W (rpi-pico2-w) included — with deployment-consistent statuses (protected candidate · HIL pending vs qualified public release) and a Pico row in the no-release grid; the hero drops the stale 'ESP32-C3 and Raspberry Pi Pico 2 W ... remain unavailable' sentence for five-profile releases and states instead that all five targets are staged pending hardware validation (qualified mode names all five profiles); the support page mirrors a staged candidate instead of claiming the installer is unavailable, and derives the ESP32-C3 note from the release profile set; and the real-hardware board photo stays on the home page whenever the selected release includes the exact-board profile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016KVSKkd7KUMHqKXDhPXTj5 Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent e57de9d commit d788832

1 file changed

Lines changed: 145 additions & 1 deletion

File tree

‎tools/web/src/test/site-contract.test.tsx‎

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,26 @@ function hypotheticalQualifiedV060Release(): FirmwareReleaseDescriptor {
7575
return release as FirmwareReleaseDescriptor;
7676
}
7777

78+
function candidateV060Release(): FirmwareReleaseDescriptor {
79+
const release = hypotheticalQualifiedV060Release() as unknown as {
80+
deployment: string;
81+
accessControlled: boolean;
82+
hilStatus: string;
83+
};
84+
release.deployment = "candidate";
85+
release.accessControlled = true;
86+
release.hilStatus = "pending";
87+
return release as unknown as FirmwareReleaseDescriptor;
88+
}
89+
90+
const fiveProfileOrder = [
91+
"esp32-4mb",
92+
"esp32-s3-n16r8",
93+
"waveshare-esp32-s3-lcd-147b",
94+
"esp32-c3-4mb",
95+
"rpi-pico2-w",
96+
] as const;
97+
7898
function jpegDimensions(bytes: Buffer): { width: number; height: number } {
7999
if (bytes.length < 4 || bytes[0] !== 0xff || bytes[1] !== 0xd8) {
80100
throw new Error("not a JPEG");
@@ -706,7 +726,36 @@ describe("public-site contract", () => {
706726
status: "Planned · installer unavailable pending exact-profile HIL",
707727
planned: true,
708728
},
729+
{
730+
id: "rpi-pico2-w",
731+
target: "Raspberry Pi Pico 2 W",
732+
constraint: "RP2350 + CYW43439 · UF2 / BOOTSEL",
733+
status: "Planned · installer unavailable pending exact-profile HIL",
734+
planned: true,
735+
},
709736
]);
737+
expect(
738+
firmwareTargetsForRelease(candidateV060Release()).map(
739+
({ id, planned, status }) => ({ id, planned, status }),
740+
),
741+
).toEqual(
742+
fiveProfileOrder.map((id) => ({
743+
id,
744+
planned: false,
745+
status: "v0.6.0 protected candidate · HIL pending",
746+
})),
747+
);
748+
expect(
749+
firmwareTargetsForRelease(hypotheticalQualifiedV060Release()).map(
750+
({ id, planned, status }) => ({ id, planned, status }),
751+
),
752+
).toEqual(
753+
fiveProfileOrder.map((id) => ({
754+
id,
755+
planned: false,
756+
status: "v0.6.0 qualified public release",
757+
})),
758+
);
710759
expect(
711760
firmwareTargetsForRelease(publicBetaFirmwareRelease)
712761
.filter(({ planned }) => !planned)
@@ -942,9 +991,90 @@ describe("public-site contract", () => {
942991
).toBeInTheDocument();
943992
candidateHome.unmount();
944993
render(<SupportPage />);
994+
// The support page must mirror the staged candidate instead of
995+
// contradicting /flash with an installer-unavailable claim.
996+
expect(
997+
screen.getByText(/protected candidate v0\.5\.1 is staged/i),
998+
).toBeInTheDocument();
999+
expect(
1000+
screen.queryByText(/firmware installer is currently unavailable/i),
1001+
).toBeNull();
1002+
} finally {
1003+
if (previousSelection === undefined) {
1004+
delete process.env.PYBLE_FLASH_SELECTION_FILE;
1005+
} else {
1006+
process.env.PYBLE_FLASH_SELECTION_FILE = previousSelection;
1007+
}
1008+
await rm(selectionRoot, { recursive: true, force: true });
1009+
}
1010+
});
1011+
1012+
it("presents all five v0.6.0 targets and the board photo for the staged candidate", async () => {
1013+
const selectionRoot = await mkdtemp(
1014+
join(tmpdir(), "pyble-site-v060-candidate-selection-"),
1015+
);
1016+
const selectionFile = join(selectionRoot, "selection.json");
1017+
const previousSelection = process.env.PYBLE_FLASH_SELECTION_FILE;
1018+
process.env.PYBLE_FLASH_SELECTION_FILE = selectionFile;
1019+
1020+
try {
1021+
await writeFile(
1022+
selectionFile,
1023+
JSON.stringify(candidateV060Release()),
1024+
"utf8",
1025+
);
1026+
const home = render(<HomePage />);
1027+
1028+
// The hero reflects the five-target candidate instead of the stale
1029+
// pre-v0.6.0 C3/Pico unavailability sentence.
1030+
expect(
1031+
screen.getByText(/protected candidate v0\.6\.0 is staged/i),
1032+
).toBeInTheDocument();
1033+
expect(
1034+
screen.queryByText(/remain unavailable in the public installer/i),
1035+
).toBeNull();
1036+
expect(
1037+
screen.getByText(
1038+
/all five firmware targets, including ESP32-C3 and Raspberry Pi Pico 2 W/i,
1039+
),
1040+
).toHaveTextContent(/pending hardware validation/i);
1041+
1042+
// The availability grid presents all five targets consistently with
1043+
// the candidate deployment mode.
1044+
for (const id of fiveProfileOrder) {
1045+
const card = screen.getByText(id).closest("div");
1046+
expect(card).toHaveTextContent(
1047+
"v0.6.0 protected candidate · HIL pending",
1048+
);
1049+
expect(card).not.toHaveTextContent(/planned/i);
1050+
}
1051+
expect(screen.getByText("rpi-pico2-w").closest("div")).toHaveTextContent(
1052+
"Raspberry Pi Pico 2 W",
1053+
);
1054+
expect(screen.getByText("esp32-c3-4mb").closest("div")).toHaveTextContent(
1055+
"ESP32-C3",
1056+
);
1057+
1058+
// The real-hardware board photo stays visible while the candidate
1059+
// offers the exact-board profile.
1060+
const boardPhoto = screen.getByRole("img", {
1061+
name: "Actual Waveshare ESP32-S3-LCD-1.47B displaying the PyBLE v0.5.0 boot splash and app QR",
1062+
});
1063+
expect(boardPhoto.closest("figure")).toHaveTextContent(
1064+
/Actual board.*PyBLE firmware v0\.5\.0/i,
1065+
);
1066+
1067+
home.unmount();
1068+
render(<SupportPage />);
9451069
expect(
946-
screen.getByText(/firmware installer is currently unavailable/i),
1070+
screen.getByText(/protected candidate v0\.6\.0 is staged/i),
9471071
).toBeInTheDocument();
1072+
expect(
1073+
screen.queryByText(/firmware installer is currently unavailable/i),
1074+
).toBeNull();
1075+
expect(
1076+
screen.queryByText(/ESP32-C3 is not currently available/i),
1077+
).toBeNull();
9481078
} finally {
9491079
if (previousSelection === undefined) {
9501080
delete process.env.PYBLE_FLASH_SELECTION_FILE;
@@ -977,6 +1107,20 @@ describe("public-site contract", () => {
9771107
).toBeInTheDocument();
9781108
flash.unmount();
9791109

1110+
const home = render(<HomePage />);
1111+
expect(
1112+
screen.getByText(/qualified public v0\.6\.0 firmware/i),
1113+
).toHaveTextContent(/all five exact release profiles/i);
1114+
expect(
1115+
screen.queryByText(/remain unavailable in the public installer/i),
1116+
).toBeNull();
1117+
for (const id of fiveProfileOrder) {
1118+
expect(screen.getByText(id).closest("div")).toHaveTextContent(
1119+
"v0.6.0 qualified public release",
1120+
);
1121+
}
1122+
home.unmount();
1123+
9801124
render(<SupportPage />);
9811125
const qualifiedCopy = screen.getByText(
9821126
/qualified v0\.6\.0 firmware is available/i,

0 commit comments

Comments
 (0)