From d5e634edf3ffa5a5a26374659ac69fa45d07bce9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:39:22 +0000 Subject: [PATCH 1/5] Initial plan From aab55627063e6f09bd5649fffb97dbdb75ad933d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:57:05 +0000 Subject: [PATCH 2/5] Add validation error UI display for form field errors Agent-Logs-Url: https://github.com/medium-tech/mspec/sessions/3730ceb5-8473-4879-ab95-1b08c9e56d0b Co-authored-by: b-rad-c <25362581+b-rad-c@users.noreply.github.com> --- browser2/js/src/markup.js | 31 +++++++-- .../pages/builtin-mapp-model-instance.json | 3 +- .../data/lingo/pages/builtin-mapp-model.json | 3 +- templates/mapp-py/tests/crud.spec.js | 68 ++++++++++++++++++ templates/sosh-net/tests/crud.spec.js | 69 ++++++++++++++++++- 5 files changed, 165 insertions(+), 9 deletions(-) diff --git a/browser2/js/src/markup.js b/browser2/js/src/markup.js index 7012e31..1eebcc6 100644 --- a/browser2/js/src/markup.js +++ b/browser2/js/src/markup.js @@ -924,19 +924,23 @@ const lingoFunctionLookup = { if (response.ok) { const responseData = await response.json(); // console.log('crud.create - responseData:', responseData); - return {state: 'success', item_id: responseData.id}; + return {state: 'success', item_id: responseData.id, field_errors: {}}; } else { const errorData = await response.json(); let errorMessage = `${response.status} ${response.statusText}`; + let fieldErrors = {}; if (errorData.hasOwnProperty('error') && errorData.error.hasOwnProperty('message')) { errorMessage = errorData.error.message; } + if (errorData.hasOwnProperty('error') && errorData.error.code === 'VALIDATION_ERROR' && errorData.error.hasOwnProperty('field_errors')) { + fieldErrors = errorData.error.field_errors; + } console.error('crud.create - HTTP error:', response.status, response.statusText); - return {state: 'error', error: errorMessage}; + return {state: 'error', error: errorMessage, field_errors: fieldErrors}; } } catch (error) { console.error('crud.create - network error:', error); - return {state: 'error', error: `Network error: ${error.message}`}; + return {state: 'error', error: `Network error: ${error.message}`, field_errors: {}}; } }, createArgs: _crudCreateArgs @@ -980,19 +984,23 @@ const lingoFunctionLookup = { if (response.ok) { const responseData = await response.json(); // console.log('crud.update - responseData:', responseData); - return {state: 'edited', data: responseData}; + return {state: 'edited', data: responseData, field_errors: {}}; } else { const errorData = await response.json(); let errorMessage = `${response.status} ${response.statusText}`; + let fieldErrors = {}; if (errorData.hasOwnProperty('error') && errorData.error.hasOwnProperty('message')) { errorMessage = errorData.error.message; } + if (errorData.hasOwnProperty('error') && errorData.error.code === 'VALIDATION_ERROR' && errorData.error.hasOwnProperty('field_errors')) { + fieldErrors = errorData.error.field_errors; + } console.error('crud.update - HTTP error:', response.status, response.statusText); - return {state: 'error', error: errorMessage}; + return {state: 'error', error: errorMessage, field_errors: fieldErrors}; } } catch (error) { console.error('crud.update - network error:', error); - return {state: 'error', error: `Network error: ${error.message}`}; + return {state: 'error', error: `Network error: ${error.message}`, field_errors: {}}; } }, createArgs: _crudUpdateArgs @@ -4558,6 +4566,17 @@ function createFormElement(app, element, ctx = null) { thirdCell.className = 'form-description'; thirdCell.textContent = fieldSpec.description || ''; } + + // Show field validation error if present + const fieldErrors = currentState.field_errors; + if (fieldErrors && fieldErrors[fieldKey]) { + const errorSpan = document.createElement('span'); + errorSpan.textContent = fieldErrors[fieldKey]; + errorSpan.style.color = 'red'; + errorSpan.style.fontWeight = 'bold'; + errorSpan.className = 'field-error'; + thirdCell.appendChild(errorSpan); + } row.appendChild(thirdCell); table.appendChild(row); diff --git a/src/mspec/data/lingo/pages/builtin-mapp-model-instance.json b/src/mspec/data/lingo/pages/builtin-mapp-model-instance.json index 1d8c1ee..1e2e5de 100644 --- a/src/mspec/data/lingo/pages/builtin-mapp-model-instance.json +++ b/src/mspec/data/lingo/pages/builtin-mapp-model-instance.json @@ -107,7 +107,8 @@ "default": { "state": "pending", "error": "", - "data": {} + "data": {}, + "field_errors": {} } }, "header": { diff --git a/src/mspec/data/lingo/pages/builtin-mapp-model.json b/src/mspec/data/lingo/pages/builtin-mapp-model.json index 0540b48..07f119b 100644 --- a/src/mspec/data/lingo/pages/builtin-mapp-model.json +++ b/src/mspec/data/lingo/pages/builtin-mapp-model.json @@ -82,7 +82,8 @@ "state": "initial", "error": "", "item_id": "", - "data": {} + "data": {}, + "field_errors": {} } }, "model_list": { diff --git a/templates/mapp-py/tests/crud.spec.js b/templates/mapp-py/tests/crud.spec.js index eae612e..4e65a95 100644 --- a/templates/mapp-py/tests/crud.spec.js +++ b/templates/mapp-py/tests/crud.spec.js @@ -498,3 +498,71 @@ test('test crud and list for all models', async ({ browser, crudEnv, crudSession await expect(page.locator('h1')).toContainText('::'); } }); + +test('test validation errors are displayed in form', async ({ browser, crudEnv, crudSession }) => { + const context = await browser.newContext({ storageState: crudSession.storageState }); + const page = await context.newPage(); + + await context.addCookies([{ name: 'protocol_mode', value: 'true', domain: new URL(crudEnv.host).hostname, path: '/' }]); + + // Find the first module and model that we can navigate to + const modules = crudEnv.spec.modules; + let targetModule, targetModel, targetModuleKebab, targetModelKebab; + for (const [moduleName, module] of Object.entries(modules)) { + if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; + for (const [modelName, model] of Object.entries(module.models || {})) { + if (model.hidden === true) continue; + if (model.auth && model.auth.max_models_per_user === 0) continue; + targetModule = module; + targetModel = model; + targetModuleKebab = module.name.kebab_case; + targetModelKebab = model.name.kebab_case; + break; + } + if (targetModel) break; + } + + expect(targetModel).toBeDefined(); + + // Navigate to the model create page + await page.goto(crudEnv.host); + await page.getByRole('link', { name: targetModuleKebab, exact: true }).click(); + await page.getByRole('link', { name: targetModelKebab, exact: true }).click(); + await expect(page.locator('h1')).toContainText(`:: ${targetModelKebab}`); + + // Mock the API to return a VALIDATION_ERROR for the create call + const apiUrl = `${crudEnv.host}/api/${targetModuleKebab}/${targetModelKebab}`; + const fieldErrorMessage = 'This field failed validation'; + const firstFieldName = Object.keys(targetModel.fields)[0]; + + await page.route(apiUrl, async route => { + if (route.request().method() === 'POST') { + await route.fulfill({ + status: 400, + contentType: 'application/json', + body: JSON.stringify({ + error: { + code: 'VALIDATION_ERROR', + message: 'This model has failed validation', + field_errors: { + [firstFieldName]: fieldErrorMessage + } + } + }) + }); + } else { + await route.continue(); + } + }); + + // Click submit to trigger the API call + await page.getByRole('button', { name: 'Submit' }).click(); + + // Verify the validation error message is shown in the status area + await expect(page.locator('#lingo-app')).toContainText('This model has failed validation'); + + // Verify the field error message is shown in the form's third column + const fieldError = page.locator('.field-error'); + await expect(fieldError).toBeVisible(); + await expect(fieldError).toContainText(fieldErrorMessage); +}); diff --git a/templates/sosh-net/tests/crud.spec.js b/templates/sosh-net/tests/crud.spec.js index a214b12..e506e6e 100644 --- a/templates/sosh-net/tests/crud.spec.js +++ b/templates/sosh-net/tests/crud.spec.js @@ -497,4 +497,71 @@ test('test crud and list for all models', async ({ browser, crudEnv, crudSession await page.getByRole('link', { name: crudEnv.spec.project.name.lower_case, exact: true }).click(); await expect(page.locator('h1')).toContainText('::'); } -}); \ No newline at end of file +}); +test('test validation errors are displayed in form', async ({ browser, crudEnv, crudSession }) => { + const context = await browser.newContext({ storageState: crudSession.storageState }); + const page = await context.newPage(); + + await context.addCookies([{ name: 'protocol_mode', value: 'true', domain: new URL(crudEnv.host).hostname, path: '/' }]); + + // Find the first module and model that we can navigate to + const modules = crudEnv.spec.modules; + let targetModule, targetModel, targetModuleKebab, targetModelKebab; + for (const [moduleName, module] of Object.entries(modules)) { + if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; + for (const [modelName, model] of Object.entries(module.models || {})) { + if (model.hidden === true) continue; + if (model.auth && model.auth.max_models_per_user === 0) continue; + targetModule = module; + targetModel = model; + targetModuleKebab = module.name.kebab_case; + targetModelKebab = model.name.kebab_case; + break; + } + if (targetModel) break; + } + + expect(targetModel).toBeDefined(); + + // Navigate to the model create page + await page.goto(crudEnv.host); + await page.getByRole('link', { name: targetModuleKebab, exact: true }).click(); + await page.getByRole('link', { name: targetModelKebab, exact: true }).click(); + await expect(page.locator('h1')).toContainText(`:: ${targetModelKebab}`); + + // Mock the API to return a VALIDATION_ERROR for the create call + const apiUrl = `${crudEnv.host}/api/${targetModuleKebab}/${targetModelKebab}`; + const fieldErrorMessage = 'This field failed validation'; + const firstFieldName = Object.keys(targetModel.fields)[0]; + + await page.route(apiUrl, async route => { + if (route.request().method() === 'POST') { + await route.fulfill({ + status: 400, + contentType: 'application/json', + body: JSON.stringify({ + error: { + code: 'VALIDATION_ERROR', + message: 'This model has failed validation', + field_errors: { + [firstFieldName]: fieldErrorMessage + } + } + }) + }); + } else { + await route.continue(); + } + }); + + // Click submit to trigger the API call + await page.getByRole('button', { name: 'Submit' }).click(); + + // Verify the validation error message is shown in the status area + await expect(page.locator('#lingo-app')).toContainText('This model has failed validation'); + + // Verify the field error message is shown in the form's third column + const fieldError = page.locator('.field-error'); + await expect(fieldError).toBeVisible(); + await expect(fieldError).toContainText(fieldErrorMessage); +}); From 2ceeeb3c2d0e0147285b45fbe278bd22edb727ed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 05:58:54 +0000 Subject: [PATCH 3/5] Use CSS class for field-error styling, cleanup description textNode Agent-Logs-Url: https://github.com/medium-tech/mspec/sessions/3730ceb5-8473-4879-ab95-1b08c9e56d0b Co-authored-by: b-rad-c <25362581+b-rad-c@users.noreply.github.com> --- browser2/js/src/markup.js | 6 +++--- browser2/js/src/style.css | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/browser2/js/src/markup.js b/browser2/js/src/markup.js index 1eebcc6..f9efa4f 100644 --- a/browser2/js/src/markup.js +++ b/browser2/js/src/markup.js @@ -4564,7 +4564,9 @@ function createFormElement(app, element, ctx = null) { } else { // Description for non-list fields thirdCell.className = 'form-description'; - thirdCell.textContent = fieldSpec.description || ''; + if (fieldSpec.description) { + thirdCell.appendChild(document.createTextNode(fieldSpec.description)); + } } // Show field validation error if present @@ -4572,8 +4574,6 @@ function createFormElement(app, element, ctx = null) { if (fieldErrors && fieldErrors[fieldKey]) { const errorSpan = document.createElement('span'); errorSpan.textContent = fieldErrors[fieldKey]; - errorSpan.style.color = 'red'; - errorSpan.style.fontWeight = 'bold'; errorSpan.className = 'field-error'; thirdCell.appendChild(errorSpan); } diff --git a/browser2/js/src/style.css b/browser2/js/src/style.css index d2daf46..ab4ada4 100644 --- a/browser2/js/src/style.css +++ b/browser2/js/src/style.css @@ -182,4 +182,9 @@ table td { padding: 20px; border-radius: 5px; z-index: 100; -} \ No newline at end of file +} +/* Field validation error styles */ +.field-error { + color: red; + font-weight: bold; +} From 1321867c45a57a0f8737be2d7d344c6b04255aeb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:26:30 +0000 Subject: [PATCH 4/5] Keep edit form visible on validation error, add edit form validation test Agent-Logs-Url: https://github.com/medium-tech/mspec/sessions/67a911c3-82b1-4047-a7e8-6d7943e0959c Co-authored-by: b-rad-c <25362581+b-rad-c@users.noreply.github.com> --- browser2/js/src/markup.js | 14 ++-- templates/mapp-py/tests/crud.spec.js | 99 +++++++++++++++++++++++++++ templates/sosh-net/tests/crud.spec.js | 99 +++++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 5 deletions(-) diff --git a/browser2/js/src/markup.js b/browser2/js/src/markup.js index f9efa4f..e5300a2 100644 --- a/browser2/js/src/markup.js +++ b/browser2/js/src/markup.js @@ -2553,6 +2553,10 @@ function _renderModelRead(app, element, ctx = null) { if (!state.hasOwnProperty('data')) state.data = null; if (!state.hasOwnProperty('state')) state.state = 'pending'; if (!state.hasOwnProperty('error')) state.error = ''; + if (!state.hasOwnProperty('field_errors')) state.field_errors = {}; + + // true when a validation error was returned during an edit operation + const isEditError = state.state === 'error' && state.field_errors && Object.keys(state.field_errors).length > 0; // // buttons @@ -2577,7 +2581,7 @@ function _renderModelRead(app, element, ctx = null) { elements.push({ button: loadScript, text: 'load', - disabled: state.state === 'editing' || state.state === 'loading' + disabled: state.state === 'editing' || state.state === 'loading' || isEditError }); // edit // @@ -2596,14 +2600,14 @@ function _renderModelRead(app, element, ctx = null) { // cancel // const cancelScript = { - set: {state: {[stateField]: {state: {}}}}, - to: 'loaded' + set: {state: {[stateField]: {}}}, + to: {state: 'loaded', field_errors: {}} }; elements.push({ button: cancelScript, text: 'cancel', - disabled: state.state !== 'editing' + disabled: state.state !== 'editing' && !isEditError }); // status // @@ -2657,7 +2661,7 @@ function _renderModelRead(app, element, ctx = null) { // view item // - if (state.state === 'editing') { + if (state.state === 'editing' || isEditError) { // view editable form elements.push({ form: { diff --git a/templates/mapp-py/tests/crud.spec.js b/templates/mapp-py/tests/crud.spec.js index 4e65a95..469f701 100644 --- a/templates/mapp-py/tests/crud.spec.js +++ b/templates/mapp-py/tests/crud.spec.js @@ -566,3 +566,102 @@ test('test validation errors are displayed in form', async ({ browser, crudEnv, await expect(fieldError).toBeVisible(); await expect(fieldError).toContainText(fieldErrorMessage); }); + +test('test validation errors are displayed in edit form', async ({ browser, crudEnv, crudSession }) => { + const context = await browser.newContext({ storageState: crudSession.storageState }); + const page = await context.newPage(); + + await context.addCookies([{ name: 'protocol_mode', value: 'true', domain: new URL(crudEnv.host).hostname, path: '/' }]); + + // Find the first module and model that we can navigate to + const modules = crudEnv.spec.modules; + let targetModule, targetModel, targetModuleKebab, targetModelKebab; + for (const [moduleName, module] of Object.entries(modules)) { + if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; + for (const [modelName, model] of Object.entries(module.models || {})) { + if (model.hidden === true) continue; + if (model.auth && model.auth.max_models_per_user === 0) continue; + targetModule = module; + targetModel = model; + targetModuleKebab = module.name.kebab_case; + targetModelKebab = model.name.kebab_case; + break; + } + if (targetModel) break; + } + + expect(targetModel).toBeDefined(); + + // Create a real item first via the API so we have an item to edit + const createExample = getExampleFromModel(targetModel, 0); + const apiUrl = `${crudEnv.host}/api/${targetModuleKebab}/${targetModelKebab}`; + const createResponse = await fetch(apiUrl, { + method: 'POST', + headers: {'Content-Type': 'application/json', 'Cookie': `session=${crudSession.storageState.cookies.find(c => c.name === 'session')?.value || ''}`}, + body: JSON.stringify(createExample) + }); + const createdItem = await createResponse.json(); + const itemId = createdItem.id; + expect(itemId).toBeDefined(); + + // Navigate to the item instance page + await page.goto(`${crudEnv.host}/${targetModuleKebab}/${targetModelKebab}/${itemId}`); + await expect(page.locator('h1')).toContainText(`:: ${targetModelKebab}`); + + // Wait for the item to load then click edit + await page.getByRole('button', { name: 'load', exact: true }).click(); + await expect(page.locator('#lingo-app')).toContainText('loaded'); + await page.getByRole('button', { name: 'edit', exact: true }).click(); + + // Verify the form is now visible + await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible(); + + // Mock the PUT endpoint to return a VALIDATION_ERROR + const itemApiUrl = `${crudEnv.host}/api/${targetModuleKebab}/${targetModelKebab}/${itemId}`; + const fieldErrorMessage = 'This field failed validation during edit'; + const firstFieldName = Object.keys(targetModel.fields)[0]; + + await page.route(itemApiUrl, async route => { + if (route.request().method() === 'PUT') { + await route.fulfill({ + status: 400, + contentType: 'application/json', + body: JSON.stringify({ + error: { + code: 'VALIDATION_ERROR', + message: 'This model has failed validation', + field_errors: { + [firstFieldName]: fieldErrorMessage + } + } + }) + }); + } else { + await route.continue(); + } + }); + + // Click submit to trigger the PUT with mocked VALIDATION_ERROR + await page.getByRole('button', { name: 'Submit' }).click(); + + // Verify the validation error message is shown in the status area + await expect(page.locator('#lingo-app')).toContainText('This model has failed validation'); + + // Verify the edit form is still visible (not reverted to view mode) + await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible(); + + // Verify the field error message is shown in the form's third column + const fieldError = page.locator('.field-error'); + await expect(fieldError).toBeVisible(); + await expect(fieldError).toContainText(fieldErrorMessage); + + // Verify the cancel button is enabled so user can exit edit mode + await expect(page.getByRole('button', { name: 'cancel', exact: true })).toBeEnabled(); + + // Click cancel to exit edit mode - field errors should clear + await page.getByRole('button', { name: 'cancel', exact: true }).click(); + + // Verify the form is gone and we are back in view mode with no field errors + await expect(page.getByRole('button', { name: 'Submit' })).not.toBeVisible(); + await expect(page.locator('.field-error')).not.toBeVisible(); +}); diff --git a/templates/sosh-net/tests/crud.spec.js b/templates/sosh-net/tests/crud.spec.js index e506e6e..90451e0 100644 --- a/templates/sosh-net/tests/crud.spec.js +++ b/templates/sosh-net/tests/crud.spec.js @@ -565,3 +565,102 @@ test('test validation errors are displayed in form', async ({ browser, crudEnv, await expect(fieldError).toBeVisible(); await expect(fieldError).toContainText(fieldErrorMessage); }); + +test('test validation errors are displayed in edit form', async ({ browser, crudEnv, crudSession }) => { + const context = await browser.newContext({ storageState: crudSession.storageState }); + const page = await context.newPage(); + + await context.addCookies([{ name: 'protocol_mode', value: 'true', domain: new URL(crudEnv.host).hostname, path: '/' }]); + + // Find the first module and model that we can navigate to + const modules = crudEnv.spec.modules; + let targetModule, targetModel, targetModuleKebab, targetModelKebab; + for (const [moduleName, module] of Object.entries(modules)) { + if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; + for (const [modelName, model] of Object.entries(module.models || {})) { + if (model.hidden === true) continue; + if (model.auth && model.auth.max_models_per_user === 0) continue; + targetModule = module; + targetModel = model; + targetModuleKebab = module.name.kebab_case; + targetModelKebab = model.name.kebab_case; + break; + } + if (targetModel) break; + } + + expect(targetModel).toBeDefined(); + + // Create a real item first via the API so we have an item to edit + const createExample = getExampleFromModel(targetModel, 0); + const apiUrl = `${crudEnv.host}/api/${targetModuleKebab}/${targetModelKebab}`; + const createResponse = await fetch(apiUrl, { + method: 'POST', + headers: {'Content-Type': 'application/json', 'Cookie': `session=${crudSession.storageState.cookies.find(c => c.name === 'session')?.value || ''}`}, + body: JSON.stringify(createExample) + }); + const createdItem = await createResponse.json(); + const itemId = createdItem.id; + expect(itemId).toBeDefined(); + + // Navigate to the item instance page + await page.goto(`${crudEnv.host}/${targetModuleKebab}/${targetModelKebab}/${itemId}`); + await expect(page.locator('h1')).toContainText(`:: ${targetModelKebab}`); + + // Wait for the item to load then click edit + await page.getByRole('button', { name: 'load', exact: true }).click(); + await expect(page.locator('#lingo-app')).toContainText('loaded'); + await page.getByRole('button', { name: 'edit', exact: true }).click(); + + // Verify the form is now visible + await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible(); + + // Mock the PUT endpoint to return a VALIDATION_ERROR + const itemApiUrl = `${crudEnv.host}/api/${targetModuleKebab}/${targetModelKebab}/${itemId}`; + const fieldErrorMessage = 'This field failed validation during edit'; + const firstFieldName = Object.keys(targetModel.fields)[0]; + + await page.route(itemApiUrl, async route => { + if (route.request().method() === 'PUT') { + await route.fulfill({ + status: 400, + contentType: 'application/json', + body: JSON.stringify({ + error: { + code: 'VALIDATION_ERROR', + message: 'This model has failed validation', + field_errors: { + [firstFieldName]: fieldErrorMessage + } + } + }) + }); + } else { + await route.continue(); + } + }); + + // Click submit to trigger the PUT with mocked VALIDATION_ERROR + await page.getByRole('button', { name: 'Submit' }).click(); + + // Verify the validation error message is shown in the status area + await expect(page.locator('#lingo-app')).toContainText('This model has failed validation'); + + // Verify the edit form is still visible (not reverted to view mode) + await expect(page.getByRole('button', { name: 'Submit' })).toBeVisible(); + + // Verify the field error message is shown in the form's third column + const fieldError = page.locator('.field-error'); + await expect(fieldError).toBeVisible(); + await expect(fieldError).toContainText(fieldErrorMessage); + + // Verify the cancel button is enabled so user can exit edit mode + await expect(page.getByRole('button', { name: 'cancel', exact: true })).toBeEnabled(); + + // Click cancel to exit edit mode - field errors should clear + await page.getByRole('button', { name: 'cancel', exact: true }).click(); + + // Verify the form is gone and we are back in view mode with no field errors + await expect(page.getByRole('button', { name: 'Submit' })).not.toBeVisible(); + await expect(page.locator('.field-error')).not.toBeVisible(); +}); From bd118dc125e20f2aec403b5cec253bfa1615d835 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:28:20 +0000 Subject: [PATCH 5/5] Remove unused targetModule variable from validation error tests Agent-Logs-Url: https://github.com/medium-tech/mspec/sessions/67a911c3-82b1-4047-a7e8-6d7943e0959c Co-authored-by: b-rad-c <25362581+b-rad-c@users.noreply.github.com> --- templates/mapp-py/tests/crud.spec.js | 8 ++++---- templates/sosh-net/tests/crud.spec.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/mapp-py/tests/crud.spec.js b/templates/mapp-py/tests/crud.spec.js index 469f701..59059cc 100644 --- a/templates/mapp-py/tests/crud.spec.js +++ b/templates/mapp-py/tests/crud.spec.js @@ -507,13 +507,13 @@ test('test validation errors are displayed in form', async ({ browser, crudEnv, // Find the first module and model that we can navigate to const modules = crudEnv.spec.modules; - let targetModule, targetModel, targetModuleKebab, targetModelKebab; + let targetModel, targetModuleKebab, targetModelKebab; for (const [moduleName, module] of Object.entries(modules)) { if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; for (const [modelName, model] of Object.entries(module.models || {})) { if (model.hidden === true) continue; if (model.auth && model.auth.max_models_per_user === 0) continue; - targetModule = module; + targetModel = model; targetModuleKebab = module.name.kebab_case; targetModelKebab = model.name.kebab_case; @@ -575,13 +575,13 @@ test('test validation errors are displayed in edit form', async ({ browser, crud // Find the first module and model that we can navigate to const modules = crudEnv.spec.modules; - let targetModule, targetModel, targetModuleKebab, targetModelKebab; + let targetModel, targetModuleKebab, targetModelKebab; for (const [moduleName, module] of Object.entries(modules)) { if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; for (const [modelName, model] of Object.entries(module.models || {})) { if (model.hidden === true) continue; if (model.auth && model.auth.max_models_per_user === 0) continue; - targetModule = module; + targetModel = model; targetModuleKebab = module.name.kebab_case; targetModelKebab = model.name.kebab_case; diff --git a/templates/sosh-net/tests/crud.spec.js b/templates/sosh-net/tests/crud.spec.js index 90451e0..bcec480 100644 --- a/templates/sosh-net/tests/crud.spec.js +++ b/templates/sosh-net/tests/crud.spec.js @@ -506,13 +506,13 @@ test('test validation errors are displayed in form', async ({ browser, crudEnv, // Find the first module and model that we can navigate to const modules = crudEnv.spec.modules; - let targetModule, targetModel, targetModuleKebab, targetModelKebab; + let targetModel, targetModuleKebab, targetModelKebab; for (const [moduleName, module] of Object.entries(modules)) { if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; for (const [modelName, model] of Object.entries(module.models || {})) { if (model.hidden === true) continue; if (model.auth && model.auth.max_models_per_user === 0) continue; - targetModule = module; + targetModel = model; targetModuleKebab = module.name.kebab_case; targetModelKebab = model.name.kebab_case; @@ -574,13 +574,13 @@ test('test validation errors are displayed in edit form', async ({ browser, crud // Find the first module and model that we can navigate to const modules = crudEnv.spec.modules; - let targetModule, targetModel, targetModuleKebab, targetModelKebab; + let targetModel, targetModuleKebab, targetModelKebab; for (const [moduleName, module] of Object.entries(modules)) { if (['auth', 'file-system', 'media'].includes(module.name.kebab_case)) continue; for (const [modelName, model] of Object.entries(module.models || {})) { if (model.hidden === true) continue; if (model.auth && model.auth.max_models_per_user === 0) continue; - targetModule = module; + targetModel = model; targetModuleKebab = module.name.kebab_case; targetModelKebab = model.name.kebab_case;