test: polish soap wizard test run flow

This commit is contained in:
a.tolmachev
2026-04-11 02:01:54 +03:00
parent e60d848293
commit feb749b7cf
6 changed files with 263 additions and 46 deletions
+3
View File
@@ -1553,6 +1553,9 @@ function currentUpstream() {
var customName = textValue('new-upstream-name');
var customUrl = textValue('new-upstream-url');
if (!customUrl && wizardProtocol === 'soap') {
customUrl = textValue('soap-endpoint-override');
}
if (!customUrl) return null;
var authMode = textValue('new-upstream-auth-mode') || 'none';
+53
View File
@@ -103,6 +103,59 @@ const server = http.createServer((request, response) => {
return;
}
if (url.pathname === '/soap/leads' && request.method === 'POST') {
let buffer = '';
request.setEncoding('utf8');
request.on('data', (chunk) => {
buffer += chunk;
});
request.on('end', () => {
const hasEmail = buffer.includes('<email>user@example.com</email>')
|| buffer.includes('<m:email>user@example.com</m:email>');
if (!hasEmail) {
response.writeHead(400, {
'Content-Type': 'text/xml; charset=utf-8',
'Cache-Control': 'no-store',
});
response.end([
'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">',
'<soap:Body>',
'<soap:Fault>',
'<faultcode>Client</faultcode>',
'<faultstring>missing email</faultstring>',
'</soap:Fault>',
'</soap:Body>',
'</soap:Envelope>',
].join(''));
return;
}
response.writeHead(200, {
'Content-Type': 'text/xml; charset=utf-8',
'Cache-Control': 'no-store',
});
response.end([
'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">',
'<soap:Body>',
'<CreateLeadResponse>',
'<id>lead_soap_123</id>',
'<status>created</status>',
'</CreateLeadResponse>',
'</soap:Body>',
'</soap:Envelope>',
].join(''));
});
request.on('error', () => {
response.writeHead(500, {
'Content-Type': 'text/plain; charset=utf-8',
'Cache-Control': 'no-store',
});
response.end('fixture_error');
});
return;
}
writeJson(response, 404, { error: 'not_found' });
});
+80
View File
@@ -213,6 +213,84 @@ function buildRestWindowOperationPayload(name) {
};
}
const SOAP_TEST_WSDL = `<?xml version="1.0"?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="urn:crm"
targetNamespace="urn:crm">
<binding name="LeadBinding" type="tns:LeadPortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="CreateLead">
<soap:operation soapAction="urn:createLead"/>
</operation>
</binding>
<service name="LeadService">
<port name="LeadPort" binding="tns:LeadBinding">
<soap:address location="http://127.0.0.1:${STREAM_FIXTURE_PORT}/soap/leads"/>
</port>
</service>
</definitions>`;
function buildSoapOperationPayload(name) {
return {
name,
display_name: 'Playwright SOAP Lead',
category: 'sales',
protocol: 'soap',
target: {
kind: 'soap',
wsdl_ref: 'sample_wsdl_pending',
service_name: 'Service',
port_name: 'Port',
operation_name: 'Operation',
endpoint_override: `http://127.0.0.1:${STREAM_FIXTURE_PORT}/soap/leads`,
soap_version: 'soap_11',
soap_action: 'urn:createLead',
binding_style: 'document_literal',
headers: [],
metadata: {
input_part_names: ['CreateLeadRequest'],
output_part_names: ['CreateLeadResponse'],
namespaces: ['urn:crm'],
},
},
input_schema: schemaObject({
email: schemaString(true),
}),
output_schema: schemaObject({
id: schemaString(true),
}),
input_mapping: {
rules: [
{
source: '$.mcp.email',
target: '$.request.body.email',
required: true,
},
],
},
output_mapping: {
rules: [
{
source: '$.response.body.id',
target: '$.output.id',
required: true,
},
],
},
execution_config: {
timeout_ms: 1000,
headers: {},
},
tool_description: {
title: 'Playwright SOAP Lead',
description: 'Creates a lead through the local SOAP fixture.',
tags: ['playwright', 'soap'],
examples: [{ input: { email: 'user@example.com' } }],
},
};
}
function buildGrpcSessionOperationPayload(name) {
return {
name,
@@ -474,10 +552,12 @@ async function mcpToolCall(session, toolName, argumentsValue) {
module.exports = {
ADMIN_EMAIL,
ADMIN_PASSWORD,
SOAP_TEST_WSDL,
browserJson,
buildGrpcSessionOperationPayload,
buildRestAsyncJobOperationPayload,
buildRestWindowOperationPayload,
buildSoapOperationPayload,
createAgent,
createOperation,
createPlatformApiKey,
+83 -1
View File
@@ -1,5 +1,13 @@
const { test, expect } = require('@playwright/test');
const { login, localized } = require('./helpers');
const {
SOAP_TEST_WSDL,
buildSoapOperationPayload,
createOperation,
getCurrentWorkspace,
login,
localized,
uniqueName,
} = require('./helpers');
test('wizard loads and protocol selection updates flow', async ({ page }) => {
await login(page);
@@ -11,3 +19,77 @@ test('wizard loads and protocol selection updates flow', async ({ page }) => {
await expect(page.locator('[data-step-counter]')).toContainText(/2/);
await expect(page.locator('#step-panel-2 .step-panel-title')).toContainText(localized('Select upstream', 'Выберите upstream'));
});
test('soap wizard uploads wsdl, applies discovered binding and runs test', async ({ page }) => {
await login(page);
const workspace = await getCurrentWorkspace(page);
const operation = await createOperation(
page,
workspace.id,
buildSoapOperationPayload(uniqueName('playwright_soap_wizard')),
);
await page.goto(`/wizard/?mode=edit&operationId=${encodeURIComponent(operation.operation_id)}`);
await page.locator('#btn-continue').click();
await page.locator('#btn-continue').click();
await expect(page.locator('#step-panel-3-soap .step-panel-title')).toContainText(/WSDL/i);
await page.evaluate((wsdl) => {
const fileName = document.getElementById('wizard-soap-wsdl-name');
if (fileName) fileName.textContent = 'lead.wsdl';
}, SOAP_TEST_WSDL);
await page.evaluate(async ({ workspaceId, operationId, wsdl }) => {
const uploaded = await window.CrankApi.uploadWsdlFile(
workspaceId,
operationId,
new TextEncoder().encode(wsdl),
'lead.wsdl',
);
const services = await window.CrankApi.listSoapServices(
workspaceId,
operationId,
null,
);
window.renderSoapServiceCatalog(services.services || []);
return uploaded;
}, {
workspaceId: workspace.id,
operationId: operation.operation_id,
wsdl: SOAP_TEST_WSDL,
});
await expect(page.locator('#wizard-soap-services-list button')).toContainText('CreateLead');
await page.locator('#wizard-soap-services-list button').getByText('CreateLead').click();
await expect(page.locator('#soap-service-name')).toHaveValue('LeadService');
await expect(page.locator('#soap-port-name')).toHaveValue('LeadPort');
await expect(page.locator('#soap-operation-name')).toHaveValue('CreateLead');
await page.locator('#btn-continue').click();
await expect(page.locator('#step-panel-4 .step-panel-title')).toBeVisible();
await page.locator('#tool-input-schema').fill(JSON.stringify({
type: 'object',
required: ['email'],
properties: {
email: { type: 'string' },
},
}, null, 2));
await page.locator('#tool-output-schema').fill(JSON.stringify({
type: 'object',
required: ['id'],
properties: {
id: { type: 'string' },
},
}, null, 2));
await page.locator('#btn-continue').click();
await expect(page.locator('[data-i18n="wizard.step5.test_title"]')).toContainText(localized('Test run', 'Тестовый запуск'));
await page.locator('#tool-input-mapping').fill('email: "$.input.email"');
await page.locator('#tool-output-mapping').fill('id: "$.response.body.id"');
await page.locator('#tool-exec-config').fill('timeout_ms: 1000');
await page.locator('#wizard-test-input').fill(JSON.stringify({ email: 'user@example.com' }, null, 2));
await page.locator('#wizard-run-test').click();
await expect(page.locator('#wizard-test-request-preview')).toHaveValue(/user@example.com/);
await expect(page.locator('#wizard-test-response-preview')).toHaveValue(/lead_soap_123/);
await expect(page.locator('#wizard-test-errors')).toHaveValue('[]');
});