Issue #250: enforce primary-device-only audio policy guardrail
Some checks failed
CI / test-and-quality (push) Has been cancelled
CI / test-and-quality (pull_request) Successful in 3m20s

This commit is contained in:
2026-03-02 01:53:40 +00:00
parent 1f98f01283
commit dc6af7547c
5 changed files with 59 additions and 0 deletions

View File

@@ -443,4 +443,35 @@ describe('PlayerShellComponent gameplay wiring', () => {
secondComponent.ngOnDestroy();
await expect(mediaPrototype.play()).rejects.toThrow('original play');
});
it('does not trigger original media play during player-shell init path', () => {
const originalPlay = vi.fn().mockResolvedValue(undefined);
const mediaPrototype = { play: originalPlay };
vi.stubGlobal('window', {
location: { hash: '', search: '' },
history: { state: null, replaceState: vi.fn() },
localStorage: { getItem: vi.fn().mockReturnValue('en'), setItem: vi.fn(), removeItem: vi.fn() },
sessionStorage: { getItem: vi.fn().mockReturnValue(null), setItem: vi.fn(), removeItem: vi.fn() },
HTMLMediaElement: { prototype: mediaPrototype },
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
});
vi.stubGlobal('navigator', { language: 'en-US', onLine: true });
const component = new PlayerShellComponent();
component.ngOnInit();
expect(originalPlay).not.toHaveBeenCalled();
component.ngOnDestroy();
});
it('exposes i18n warning copy for phone-client audio policy', () => {
const component = new PlayerShellComponent();
expect(component.copy('player.audio_policy_notice')).not.toBe('player.audio_policy_notice');
expect(component.copy('player.audio_policy_notice')).toContain('Audio');
});
});

View File

@@ -45,6 +45,7 @@ function resolveLocalStorage(): Storage | undefined {
imports: [CommonModule, FormsModule],
template: `
<h2>{{ copy('player.title') }}</h2>
<p *ngIf="clientHasNoAudioOutput" class="hint">{{ copy('player.audio_policy_notice') }}</p>
<div class="panel" [attr.data-client-has-no-audio-output]="clientHasNoAudioOutput">
<label>{{ copy('common.session_code') }} <input [(ngModel)]="sessionCode" /></label>