diff --git a/README.md b/README.md index 96dfd20..3559265 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Append these query parameters to the URL to customize the look and data of your | `height` | Number | Sets the height of the SVG in pixels. | `300` | `?height=350` | | `stroke` | Boolean | Adds an outline stroke to chart segments. | `false` | `?stroke=true` | | `test` | Boolean | Uses samples data instead of fetching from GitHub API. | `false` | `?test=true` | +| `error` | String | Forces an error SVG with the given message. For testing only. | — | `?error=test` | #### Example URL To get 10 languages, a dark theme, and a custom title: @@ -110,5 +111,14 @@ vercel dev > The default endpoint is /api/languages +## Error Responses + +All errors return HTTP 200 with an error SVG so they render in GitHub README embeds. + +Common error messages: +- `GitHub API error: {status} {statusText}` — GitHub API request failed +- `No language data available` — no public repositories found +- `At least one of GITHUB_USERNAMES or GITHUB_ORGS must be set` — missing environment configuration + ## License MIT License - see [LICENSE](./LICENSE) for details. diff --git a/api/languages/index.ts b/api/languages/index.ts index 9d4c286..36ff84e 100644 --- a/api/languages/index.ts +++ b/api/languages/index.ts @@ -13,10 +13,12 @@ export default async function handler( chartType, chartTitle, width, height, count, selectedTheme, stroke, - useTestData + useTestData, errorTest } = parseQueryParams(req.query as QueryParams); try { + if (errorTest) throw new Error(errorTest); + const rawData = await fetchLanguageData(useTestData); const normalizedData = processLanguageData(rawData, count); const { segments, legend } = generateChartData(normalizedData, selectedTheme, chartType, width, stroke); diff --git a/src/utils/params.ts b/src/utils/params.ts index c3bb486..aa431b4 100644 --- a/src/utils/params.ts +++ b/src/utils/params.ts @@ -41,6 +41,7 @@ export function parseQueryParams(query: QueryParams) { colours: customColours }, stroke: query["stroke"] === "true", - useTestData: query["test"] === "true" + useTestData: query["test"] === "true", + errorTest: sanitize(query["error"] ?? '') } } diff --git a/tests/api/languages/index.test.ts b/tests/api/languages/index.test.ts index ae24dff..db74b4e 100644 --- a/tests/api/languages/index.test.ts +++ b/tests/api/languages/index.test.ts @@ -33,15 +33,18 @@ describe("handler", () => { send: vi.fn() } as unknown as VercelResponse; + vi.spyOn(console, 'error').mockImplementation(() => {}); + vi.mocked(parseQueryParams).mockReturnValue({ - chartType: "donut", - chartTitle: "Languages", - width: 600, - height: 400, - count: 5, + chartType: "donut", + chartTitle: "Languages", + width: 600, + height: 400, + count: 5, selectedTheme: mockTheme, - stroke: false, - useTestData: false + stroke: false, + useTestData: false, + errorTest: '' }); }); @@ -106,4 +109,20 @@ describe("handler", () => { expect(res.setHeader).toHaveBeenCalledWith("X-Chart-Error", "true"); expect(res.status).toHaveBeenCalledWith(200); }); + + it("throws error when errorTest param is set", async () => { + vi.mocked(parseQueryParams).mockReturnValue({ + chartType: "donut", chartTitle: "Languages", + width: 600, height: 400, count: 5, + selectedTheme: mockTheme, stroke: false, + useTestData: false, errorTest: "test error" + }); + const errorSvg = "error"; + vi.mocked(renderError).mockReturnValue(errorSvg); + + await handler(req, res); + + expect(renderError).toHaveBeenCalledWith("test error", 600, 400, mockTheme); + expect(res.status).toHaveBeenCalledWith(200); + }); });