245b76ec43
ESLint emitted 17 errors in tests/Frontend/* (production code clean):
- 13× @typescript-eslint/no-explicit-any in axios mock casts
(BulkActionsBar, ProjectsView, projectsStore specs)
- 3× vitest/no-disabled-tests rule-not-found
(eslint-plugin-vitest not registered; inline-disable comments stale)
- 1× @typescript-eslint/no-unused-vars on imported beforeEach
Plus Phase 5 audit finding: TwoFactorView.spec.ts test router was
missing /recovery-use stub → Vue Router warn on every TwoFactorView mount.
Changes:
- BulkActionsBar.spec.ts, ProjectsView.spec.ts, projectsStore.spec.ts:
replace `as any` with `as unknown as ReturnType<typeof vi.fn>` on
axios method mocks; one case used `as unknown as { regionsOpen: bool }`
for vm shape.
- NewProjectDialog.spec.ts, ProjectsView.spec.ts: remove stale
`// eslint-disable-next-line vitest/no-disabled-tests` comments
(it.skip() lines kept).
- ProjectsView.toolbar.spec.ts: drop unused `beforeEach` from import.
- TwoFactorView.spec.ts: add `/recovery-use` route stub.
Verification:
- npx eslint --max-warnings=0 → exit 0 (was 17 errors).
- npx vitest run on affected specs → 24/27 passed + 3 skipped (was same).
- TwoFactorView spec → 3/3 passed, no Vue Router warn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
155 lines
5.2 KiB
TypeScript
155 lines
5.2 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { mount } from '@vue/test-utils';
|
|
import { nextTick } from 'vue';
|
|
import DevIndexOverlay from '../../resources/js/components/DevIndexOverlay.vue';
|
|
import { useDevIndices } from '../../resources/js/composables/useDevIndices';
|
|
|
|
describe('DevIndexOverlay', () => {
|
|
beforeEach(() => useDevIndices().reset());
|
|
|
|
it('hidden when no current target', () => {
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
expect(document.querySelector('.dx-badge')).toBeNull();
|
|
});
|
|
|
|
it('shows badge with id + tag when target is set', async () => {
|
|
const el = document.createElement('button');
|
|
el.setAttribute('data-dx', '1030');
|
|
el.textContent = 'Создать';
|
|
document.body.appendChild(el);
|
|
|
|
const dx = useDevIndices();
|
|
dx.setTarget(el);
|
|
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
await nextTick();
|
|
const badge = document.querySelector('.dx-badge');
|
|
expect(badge).not.toBeNull();
|
|
expect(badge!.textContent).toContain('1030');
|
|
expect(badge!.textContent!.toLowerCase()).toContain('button');
|
|
|
|
document.body.removeChild(el);
|
|
});
|
|
|
|
it('Esc clears the target', async () => {
|
|
const el = document.createElement('div');
|
|
el.setAttribute('data-dx', '5');
|
|
document.body.appendChild(el);
|
|
const dx = useDevIndices();
|
|
dx.setTarget(el);
|
|
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
await nextTick();
|
|
|
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
await nextTick();
|
|
|
|
expect(dx.currentId.value).toBeNull();
|
|
document.body.removeChild(el);
|
|
});
|
|
|
|
it('clicking the badge copies "#<id>" to clipboard', async () => {
|
|
const writeText = vi.fn().mockResolvedValue(undefined);
|
|
Object.defineProperty(navigator, 'clipboard', {
|
|
value: { writeText },
|
|
configurable: true,
|
|
});
|
|
|
|
const el = document.createElement('button');
|
|
el.setAttribute('data-dx', '1030');
|
|
document.body.appendChild(el);
|
|
useDevIndices().setTarget(el);
|
|
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
await nextTick();
|
|
|
|
const badge = document.querySelector('.dx-badge') as HTMLElement;
|
|
expect(badge).not.toBeNull();
|
|
badge.click();
|
|
await nextTick();
|
|
expect(writeText).toHaveBeenCalledWith('#1030');
|
|
|
|
document.body.removeChild(el);
|
|
});
|
|
});
|
|
|
|
describe('DevIndexOverlay — Alt-keys + overlay-mode', () => {
|
|
beforeEach(() => useDevIndices().reset());
|
|
|
|
it('Alt+ArrowUp walks to parent', async () => {
|
|
const grand = document.createElement('div');
|
|
grand.setAttribute('data-dx', '100');
|
|
const child = document.createElement('button');
|
|
child.setAttribute('data-dx', '200');
|
|
grand.appendChild(child);
|
|
document.body.appendChild(grand);
|
|
|
|
const dx = useDevIndices();
|
|
dx.setTarget(child);
|
|
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
await nextTick();
|
|
|
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', altKey: true }));
|
|
await nextTick();
|
|
|
|
expect(dx.currentId.value).toBe(100);
|
|
document.body.removeChild(grand);
|
|
});
|
|
|
|
it('Alt+ArrowDown walks to first descendant', async () => {
|
|
const parent = document.createElement('div');
|
|
parent.setAttribute('data-dx', '1');
|
|
const child = document.createElement('span');
|
|
child.setAttribute('data-dx', '2');
|
|
parent.appendChild(child);
|
|
document.body.appendChild(parent);
|
|
|
|
const dx = useDevIndices();
|
|
dx.setTarget(parent);
|
|
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
await nextTick();
|
|
|
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', altKey: true }));
|
|
await nextTick();
|
|
|
|
expect(dx.currentId.value).toBe(2);
|
|
document.body.removeChild(parent);
|
|
});
|
|
|
|
it('Alt+Shift+I toggles overlay-mode', async () => {
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
await nextTick();
|
|
|
|
const dx = useDevIndices();
|
|
expect(dx.overlayMode.value).toBe(false);
|
|
|
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'I', altKey: true, shiftKey: true }));
|
|
await nextTick();
|
|
expect(dx.overlayMode.value).toBe(true);
|
|
|
|
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'I', altKey: true, shiftKey: true }));
|
|
await nextTick();
|
|
expect(dx.overlayMode.value).toBe(false);
|
|
});
|
|
|
|
it('overlay-mode renders mini-badges on all [data-dx] elements', async () => {
|
|
const a = document.createElement('div');
|
|
a.setAttribute('data-dx', '1');
|
|
const b = document.createElement('div');
|
|
b.setAttribute('data-dx', '2');
|
|
document.body.append(a, b);
|
|
|
|
mount(DevIndexOverlay, { attachTo: document.body });
|
|
useDevIndices().toggleOverlay();
|
|
await nextTick();
|
|
|
|
const minis = document.querySelectorAll('.dx-mini');
|
|
expect(minis.length).toBeGreaterThanOrEqual(2);
|
|
|
|
document.body.removeChild(a);
|
|
document.body.removeChild(b);
|
|
});
|
|
});
|