Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
4 changes: 3 additions & 1 deletion api/languages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/utils/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"] ?? '')
}
}
33 changes: 26 additions & 7 deletions tests/api/languages/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: ''
});
});

Expand Down Expand Up @@ -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 = "<svg>error</svg>";
vi.mocked(renderError).mockReturnValue(errorSvg);

await handler(req, res);

expect(renderError).toHaveBeenCalledWith("test error", 600, 400, mockTheme);
expect(res.status).toHaveBeenCalledWith(200);
});
});
Loading