address greptile review feedback (greploop iteration 2)

- Fix P2: add useEffect cleanup to prevent stale response race condition
- Fix P2: guard against unparseable expiration_date values (e.g. "N/A")
- Fix P2: replace fragile .ant-alert CSS selectors with queryByRole("alert")
- Add test for unparseable expiration_date

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
yuneng-jiang 2026-03-24 09:05:42 -07:00
parent 3d472aa21e
commit aad2d59c14
2 changed files with 28 additions and 3 deletions

View file

@ -90,7 +90,7 @@ describe("LicenseExpiryBanner", () => {
expect(mockGetLicenseInfo).toHaveBeenCalled();
});
await waitFor(() => {
expect(container.querySelector(".ant-alert")).toBeNull();
expect(screen.queryByRole("alert")).toBeNull();
});
});
@ -169,7 +169,19 @@ describe("LicenseExpiryBanner", () => {
expect(mockGetLicenseInfo).toHaveBeenCalled();
});
await waitFor(() => {
expect(container.querySelector(".ant-alert")).toBeNull();
expect(screen.queryByRole("alert")).toBeNull();
});
});
it("should render nothing when expiration_date is unparseable", async () => {
mockGetLicenseInfo.mockResolvedValue(makeLicense("N/A"));
const { container } = render(<LicenseExpiryBanner />);
await waitFor(() => {
expect(mockGetLicenseInfo).toHaveBeenCalled();
});
await waitFor(() => {
expect(screen.queryByRole("alert")).toBeNull();
});
});
});

View file

@ -14,9 +14,16 @@ export const LicenseExpiryBanner: React.FC = () => {
useEffect(() => {
if (!accessToken) return;
let cancelled = false;
getLicenseInfo(accessToken)
.then(setLicenseInfo)
.then((info) => {
if (!cancelled) setLicenseInfo(info);
})
.catch(() => null);
return () => {
cancelled = true;
};
}, [accessToken]);
if (!licenseInfo?.has_license || !licenseInfo.expiration_date) {
@ -27,6 +34,12 @@ export const LicenseExpiryBanner: React.FC = () => {
// then append end-of-day in UTC to avoid timezone-dependent parsing.
const dateOnly = licenseInfo.expiration_date.split("T")[0];
const expDate = new Date(dateOnly + "T23:59:59Z");
// Guard against unparseable expiration_date values (e.g. "N/A", malformed strings)
if (isNaN(expDate.getTime())) {
return null;
}
const now = new Date();
const diffMs = expDate.getTime() - now.getTime();
const daysRemaining = Math.ceil(diffMs / (1000 * 60 * 60 * 24));