caterium-app/tests/support-form.spec.mjs
pavlov346346-source c3cb44419b
Add human support form with fixed email recipient (#40)
Add Help contact form and PHP mail endpoint for support@katerion.ru, with validated Reply-To, explicit diagnostics consent, CSRF/origin checks, hashed rate limits and duplicate protection. Preserve drafts on error and avoid serializing customer data or SDK internals. Full PR QA passed in 35559317001; isolated PHP and 30 browser cases passed in 35559179372. Standard production gates unchanged. Publication checks do not send real mail; inbox receipt remains unverified. No training photo assets or unfinished training lifecycle changes.
2026-09-21 07:12:26 +03:00

55 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {test,expect} from '@playwright/test';
import fs from 'node:fs';
import {spawnSync} from 'node:child_process';
const EMAIL='support@katerion.ru';
async function fixture(page){
await page.route('**/index.html',r=>r.fulfill({contentType:'text/html',body:'<!doctype html><html><head><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><header><nav></nav></header></body></html>'}));
await page.goto('/index.html');await page.addStyleTag({url:'/core/help-center.css'});await page.addScriptTag({url:'/core/help-center.js'});
await page.getByRole('button',{name:'Помощь',exact:true}).click();await expect(page.locator('#ctContactTab')).toBeVisible();
}
async function fill(page){
await page.locator('#ctContactTab').click();await page.locator('#ctContactName').fill('Анна');await page.locator('#ctContactEmail').fill('anna@example.invalid');
await page.locator('#ctContactSubject').fill('Не получается открыть PDF');await page.locator('#ctContactMessage').fill('Как посмотреть предложение с телефона?');
}
function mock(page,answer={ok:true,status:'accepted',id:'SUP-20260921-123456ABCDEF'},http=202){
const requests=[];
return page.route('**/api/support.php',r=>{requests.push({method:r.request().method(),body:r.request().postDataJSON()});return r.fulfill({status:r.request().method()==='GET'?200:http,contentType:'application/json',body:JSON.stringify(r.request().method()==='GET'?{csrf:'token',recipient:EMAIL}:answer)});}).then(()=>requests);
}
test('PHP support delivery validates inputs and never sends a real test email',async()=>{
const result=spawnSync('php',['tests/support-mail.php'],{encoding:'utf8'});expect(result.status,result.stderr+result.stdout).toBe(0);
});
test('help exposes contact form after an unanswered search, without background mail or personal data requests',async({page})=>{
const requests=await mock(page);await fixture(page);
await page.locator('#ctHelpSearch').fill('zznomatch99381zz');await expect(page.locator('#ctHelpStatus')).toContainText('Ничего не найдено');
await page.locator('#ctHelpGuide [data-human-support]').click();await expect(page.locator('#ctContactForm')).toBeVisible();
expect(requests).toHaveLength(0);await expect(page.locator('#ctContactSection')).toContainText(EMAIL);
await page.locator('#ctHelpGuideTab').click();await expect(page.locator('#ctContactForm')).toBeHidden();await expect(page.locator('#ctHelpSearch')).toBeVisible();
});
test('explicit form submission sends the fixed-recipient contract and whitelisted diagnostics only',async({page},info)=>{
const requests=await mock(page);await fixture(page);
await page.evaluate(()=>{localStorage.sunOrders='PRIVATE_ORDER_CANARY';const sdk={token:'SECRET_TOKEN_CANARY'};sdk.self=sdk;window.SunCloudV2={getClient:()=>sdk};});
await fill(page);await page.locator('#ctContactDiagnostics').check();await page.screenshot({path:info.outputPath('support-form.png')});
expect(await page.locator('#ctContactForm').evaluate(el=>el.scrollWidth<=el.clientWidth)).toBe(true);
await page.locator('#ctContactSend').click();await expect(page.locator('#ctContactStatus')).toContainText('принято почтовым сервером');
const post=requests.find(r=>r.method==='POST').body;expect(post.email).toBe('anna@example.invalid');expect(post.message).toContain('предложение');
expect(post.device).toContain('экран');expect(post).not.toHaveProperty('to');expect(post).not.toHaveProperty('token');expect(JSON.stringify(post)).not.toMatch(/PRIVATE_ORDER_CANARY|SECRET_TOKEN_CANARY/);
await expect(page.locator('#ctContactMessage')).toHaveValue('');expect(await page.evaluate(()=>localStorage.sunOrders)).toBe('PRIVATE_ORDER_CANARY');
});
test('failed submission preserves the form and request ID; no silent mail-client fallback or false success',async({page})=>{
const requests=await mock(page,{ok:false,message:'Почтовый сервер не принял сообщение'},503);await fixture(page);await fill(page);
await page.locator('#ctContactSend').click();await expect(page.locator('#ctContactStatus')).toHaveAttribute('role','alert');await expect(page.locator('#ctContactMessage')).toHaveValue('Как посмотреть предложение с телефона?');
await page.locator('#ctContactSend').click();await expect.poll(()=>requests.filter(r=>r.method==='POST').length).toBe(2);
const posts=requests.filter(r=>r.method==='POST').map(r=>r.body);expect(posts[0].request_id).toBe(posts[1].request_id);expect(posts[0]).not.toHaveProperty('device');
await page.locator('#ctHelpClose').click();await page.getByRole('button',{name:'Помощь',exact:true}).click();await page.locator('#ctContactTab').click();await expect(page.locator('#ctContactMessage')).toHaveValue('Как посмотреть предложение с телефона?');
});
test('invalid fields never send and another account never sees the previous support draft',async({page})=>{
const requests=await mock(page);await fixture(page);await fill(page);await page.locator('#ctContactEmail').fill('invalid');await page.locator('#ctContactSend').click();expect(requests).toHaveLength(0);
await page.evaluate(()=>{window.SunCloudV2={getSession:()=>({user:{id:'another',email:'other@example.invalid'}})};window.dispatchEvent(new Event('sun:cloud-permissions-changed'));});
await page.getByRole('button',{name:'Помощь',exact:true}).click();await page.locator('#ctContactTab').click();await expect(page.locator('#ctContactMessage')).toHaveValue('');await expect(page.locator('#ctContactEmail')).toHaveValue('other@example.invalid');
});