Skip to content

Commit b296019

Browse files
committed
add hostname gating unit tests for runYoutubeAdDetection
Tests lock in expected behavior: allows youtube.com and subdomains, privacy-test-pages.site and subdomains, rejects other domains. Also adds resetYoutubeAdDetection() for test cleanup and stores the start retry timeout so stop() can clear it properly. Made-with: Cursor
1 parent 9c7b8b3 commit b296019

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

injected/src/detectors/detections/youtube-ad-detection.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export class YouTubeAdDetector {
5959
// Intervals and tracking
6060
this.pollInterval = null;
6161
this.rerootInterval = null;
62+
this.startRetryTimeout = null;
6263
this.trackedVideoElement = null;
6364
this.lastLoggedVideoId = null;
6465
this.currentVideoId = null;
@@ -615,7 +616,7 @@ export class YouTubeAdDetector {
615616
if (!root) {
616617
if (attempt < 25) {
617618
this.log.info(`Player root not found, retrying in 500ms (attempt ${attempt}/25)`);
618-
setTimeout(() => this.start(attempt + 1), 500);
619+
this.startRetryTimeout = setTimeout(() => this.start(attempt + 1), 500);
619620
} else {
620621
this.log.info('Player root not found after 25 attempts, giving up');
621622
}
@@ -651,6 +652,10 @@ export class YouTubeAdDetector {
651652
* Stop the detector
652653
*/
653654
stop() {
655+
if (this.startRetryTimeout) {
656+
clearTimeout(this.startRetryTimeout);
657+
this.startRetryTimeout = null;
658+
}
654659
if (this.pollInterval) {
655660
clearInterval(this.pollInterval);
656661
this.pollInterval = null;
@@ -763,3 +768,13 @@ export function runYoutubeAdDetection(config, logger, fireEvent) {
763768
detectorInstance.start();
764769
return detectorInstance.getResults();
765770
}
771+
772+
/**
773+
* @visibleForTesting
774+
*/
775+
export function resetYoutubeAdDetection() {
776+
if (detectorInstance) {
777+
detectorInstance.stop();
778+
detectorInstance = null;
779+
}
780+
}

injected/unit-test/youtube-ad-detection.js

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { YouTubeAdDetector } from '../src/detectors/detections/youtube-ad-detection.js';
1+
import { YouTubeAdDetector, runYoutubeAdDetection, resetYoutubeAdDetection } from '../src/detectors/detections/youtube-ad-detection.js';
22

33
const minimalConfig = {
44
playerSelectors: ['#movie_player'],
@@ -151,4 +151,82 @@ describe('YouTubeAdDetector', () => {
151151
expect(detector.state.detections.adBlocker.showing).toBe(true);
152152
});
153153
});
154+
155+
describe('runYoutubeAdDetection hostname gating', () => {
156+
const enabledConfig = { ...minimalConfig, state: 'enabled' };
157+
const emptyResult = { detected: false, type: 'youtubeAds', results: [] };
158+
let savedWindow;
159+
let savedDocument;
160+
161+
beforeEach(() => {
162+
savedWindow = globalThis.window;
163+
savedDocument = globalThis.document;
164+
});
165+
166+
afterEach(() => {
167+
resetYoutubeAdDetection();
168+
globalThis.window = savedWindow;
169+
globalThis.document = savedDocument;
170+
});
171+
172+
function setHostname(name) {
173+
const mockDoc = { querySelector: () => null, querySelectorAll: () => [], body: null, hidden: false, readyState: 'complete' };
174+
globalThis.window = /** @type {any} */ ({
175+
location: { hostname: name, search: '' },
176+
document: mockDoc,
177+
navigator: { userActivation: { isActive: false } },
178+
addEventListener: () => {},
179+
performance: { now: () => 0 },
180+
setTimeout: () => {},
181+
setInterval: () => {},
182+
URLSearchParams,
183+
});
184+
globalThis.document = /** @type {any} */ (mockDoc);
185+
}
186+
187+
it('rejects other domains', () => {
188+
setHostname('example.com');
189+
expect(runYoutubeAdDetection(enabledConfig)).toEqual(emptyResult);
190+
});
191+
192+
it('rejects domains containing youtube as a substring', () => {
193+
setHostname('notyoutube.com');
194+
expect(runYoutubeAdDetection(enabledConfig)).toEqual(emptyResult);
195+
});
196+
197+
it('rejects localhost', () => {
198+
setHostname('localhost');
199+
expect(runYoutubeAdDetection(enabledConfig)).toEqual(emptyResult);
200+
});
201+
202+
it('allows youtube.com', () => {
203+
setHostname('youtube.com');
204+
const result = runYoutubeAdDetection(enabledConfig);
205+
expect(result).not.toEqual(emptyResult);
206+
});
207+
208+
it('allows www.youtube.com', () => {
209+
setHostname('www.youtube.com');
210+
const result = runYoutubeAdDetection(enabledConfig);
211+
expect(result).not.toEqual(emptyResult);
212+
});
213+
214+
it('allows m.youtube.com', () => {
215+
setHostname('m.youtube.com');
216+
const result = runYoutubeAdDetection(enabledConfig);
217+
expect(result).not.toEqual(emptyResult);
218+
});
219+
220+
it('allows privacy-test-pages.site', () => {
221+
setHostname('privacy-test-pages.site');
222+
const result = runYoutubeAdDetection(enabledConfig);
223+
expect(result).not.toEqual(emptyResult);
224+
});
225+
226+
it('allows subdomains of privacy-test-pages.site', () => {
227+
setHostname('test.privacy-test-pages.site');
228+
const result = runYoutubeAdDetection(enabledConfig);
229+
expect(result).not.toEqual(emptyResult);
230+
});
231+
});
154232
});

0 commit comments

Comments
 (0)