Files
portal/app/tests/Frontend/AdminSupplierPricesView.spec.ts
T
Дмитрий c8005e0cfc fix(a11y): Q.DEFER.004 sub-B — AdminSupplierPricesView 9 inputs aria-label
3 supplier rows × 3 form controls (cost_rub v-text-field +
quality_score v-text-field + is_active v-switch) = 9 nodes без label —
axe-core критичная label violation.

Fix: :aria-label='${field} для ${supplier.name}' (e.g. 'Cost (₽) для B1 — Сайты и Звонки').

Test coverage: AdminSupplierPricesView.spec.ts 4-й spec проверяет все 9 ожидаемых
aria-label через DOM query.
2026-05-13 00:35:05 +03:00

83 lines
3.6 KiB
TypeScript
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 { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import { createVuetify } from 'vuetify';
import axios from 'axios';
import AdminSupplierPricesView from '../../resources/js/views/admin/AdminSupplierPricesView.vue';
vi.mock('axios');
// Auto-импорт компонентов/директив Vuetify подхватывает vite-plugin-vuetify
// из vitest.config.ts (см. AdminPricingTiersView.spec.ts).
const vuetify = createVuetify();
const mockSuppliers = [
{ id: 1, code: 'b1', name: 'B1 — Сайты и Звонки', cost_rub: '1.00', quality_score: '1.00', is_active: true },
{ id: 2, code: 'b2', name: 'B2 — SMS', cost_rub: '1.50', quality_score: '1.00', is_active: true },
{ id: 3, code: 'b3', name: 'B3 — SMS', cost_rub: '1.20', quality_score: '0.95', is_active: true },
];
describe('AdminSupplierPricesView', () => {
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(axios.get as any).mockResolvedValue({ data: { data: mockSuppliers } });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(axios.patch as any).mockResolvedValue({ data: { data: mockSuppliers[0] } });
});
it('renders 3 supplier rows', async () => {
const wrapper = mount(AdminSupplierPricesView, { global: { plugins: [vuetify] } });
await new Promise((r) => setTimeout(r, 50));
expect(wrapper.text()).toContain('b1');
expect(wrapper.text()).toContain('b2');
expect(wrapper.text()).toContain('b3');
});
it('save() fires PATCH with cost_rub/quality_score/is_active', async () => {
const wrapper = mount(AdminSupplierPricesView, { global: { plugins: [vuetify] } });
await new Promise((r) => setTimeout(r, 50));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await (wrapper.vm as any).save({
id: 1,
code: 'b1',
name: '',
cost_rub: '2.00',
quality_score: '1.00',
is_active: true,
});
expect(axios.patch).toHaveBeenCalledWith('/api/admin/suppliers/1', {
cost_rub: '2.00',
quality_score: '1.00',
is_active: true,
});
});
it('renders quality_score, cost_rub as editable text-fields', async () => {
const wrapper = mount(AdminSupplierPricesView, { global: { plugins: [vuetify] } });
await new Promise((r) => setTimeout(r, 50));
const inputs = wrapper.findAll('input[type="number"]');
expect(inputs.length).toBeGreaterThanOrEqual(6);
});
it('each input/switch has explicit aria-label combining supplier name + field role', async () => {
const wrapper = mount(AdminSupplierPricesView, { global: { plugins: [vuetify] } });
await new Promise((r) => setTimeout(r, 50));
// 3 suppliers × 3 fields = 9 controls
const expectedLabels = [
'Cost (₽) для B1 — Сайты и Звонки',
'Quality для B1 — Сайты и Звонки',
'Active для B1 — Сайты и Звонки',
'Cost (₽) для B2 — SMS',
'Quality для B2 — SMS',
'Active для B2 — SMS',
'Cost (₽) для B3 — SMS',
'Quality для B3 — SMS',
'Active для B3 — SMS',
];
for (const label of expectedLabels) {
const node = wrapper.find(`[aria-label="${label}"]`);
expect(node.exists(), `aria-label="${label}" not found`).toBe(true);
}
});
});