test(e2e): pin the provider create payload, including the fields that get dropped

The provider editor's whole write path had no oracle: createProvider,
updateProvider, deleteProvider and the two test mutations were absent from every
spec. The failure this guards is not hypothetical — the same class (a zero-ish
agent field silently lost between the form and the wire) was found and fixed in
the converter one round ago, with nothing left behind to keep it fixed.

The cassette's defaults now carry `json: true`, `n: 1` and a real maxTokens, and
the spec asserts the createProvider variables still hold them after the form
round trip, plus that all thirteen agents are serialised rather than only the
one the user touched. Dropping `n` from transformFormToGraphQL turns it red.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-07-26 04:48:08 +07:00
co-authored by Claude Opus 4.8
parent 7a56dc53aa
commit 548cc12488
2 changed files with 55 additions and 3 deletions
@@ -17,17 +17,19 @@ import { baseQueries, baseRest } from './base.ts';
const T = '2026-01-15T09:00:00Z';
/** `n: 1` and `json: true` are the shipped simple_json defaults; they are pinned in the create payload
* because dropping exactly these on the way to the wire is a regression this repo has already had. */
const agentConfig = (model = 'e2e-model'): AgentConfigFragmentFragment =>
entity('AgentConfig', {
extraBody: null,
frequencyPenalty: null,
json: null,
json: true,
maxLength: null,
maxTokens: null,
maxTokens: 4096,
minLength: null,
minP: null,
model,
n: null,
n: 1,
presencePenalty: null,
price: null,
reasoning: null,
@@ -53,3 +53,53 @@ test.describe('settings providers create form', { tag: '@coverage' }, () => {
expectCleanPage(pageErrorLog);
});
});
test.describe('settings providers write path', { tag: '@coverage' }, () => {
const CREATED_NAME = 'E2E created provider';
test.use({
cassette: populatedSettingsProvidersCassette({
mutations: {
createProvider: [
{
data: { createProvider: { __typename: 'ResultType', result: 'success' } } as never,
variables: { name: CREATED_NAME, type: 'anthropic' },
},
],
},
}),
});
// The create form is seeded from the type's defaults and then serialises the whole agents map back.
// Fields the user never touches are exactly the ones that get dropped on the way to the wire — this
// asserts the payload, because the UI looks identical either way.
test('create sends every agent field it was seeded with, zero-ish values included', async ({
page,
pageErrorLog,
}) => {
await page.goto('/settings/providers/new?type=anthropic');
await expect(page.getByLabel('Name', { exact: true })).toBeVisible();
await page.getByLabel('Name', { exact: true }).fill(CREATED_NAME);
const request = page.waitForRequest(
(candidate) =>
candidate.method() === 'POST' && candidate.postDataJSON()?.operationName === 'createProvider',
);
await page.getByRole('button', { exact: true, name: 'Create' }).click();
const { variables } = (await request).postDataJSON();
expect(variables.name).toBe(CREATED_NAME);
expect(variables.type).toBe('anthropic');
expect(Object.keys(variables.agents), 'every agent is serialised, not just the edited one').toHaveLength(13);
expect(variables.agents.simpleJson).toMatchObject({
json: true,
maxTokens: 4096,
model: 'e2e-anthropic-model',
n: 1,
});
expectCleanPage(pageErrorLog);
});
});