Selector System Overview
This document explains how user-related DOM selectors are managed, structured, and tested within the project.
📦 selectorMetadata.ts: The Source of Truth
All selectors are defined in selectorMetadata.ts. Each selector has:
selector: A CSS selector stringcontexts: One or more pages it appears ondescription(optional): Clarifies purposesmoketest(optional, default = true): If false, the selector is excluded from CI smoketests
Example:
{
selector: '.channel-root--watch h1.tw-title',
contexts: ['channelWatch'],
description: 'Live stream view title',
}
🧠 Context Definitions
| Context | Meaning |
|---|---|
homepage | Twitch front page after load |
sidebarCollapsed | Same as homepage, but nav is collapsed |
channelWatch | The initial landing page when clicking a live stream |
channelHome | The full profile view after clicking the channel name |
⚙️ selectors.ts: Auto-Generated for Extension Use
This file flattens and deduplicates all selectors across all contexts:
export const USERNAME_SELECTORS = Array.from(
new Set(SELECTOR_METADATA.map(meta => meta.selector))
);
Used by the extension’s content script for note display and interaction.
🧪 selector-check.ts: Context-Aware Smoketests
This script performs a CI smoketest that:
- Visits each relevant Twitch context
- Runs only the selectors assigned to that context
- Compares results to
last-smoketest.json - Fails CI if any selector previously
trueis nowfalse
Smoketests automatically skip any selector marked smoketest: false.
➕ How to Add a New Selector
- Add a new object to
selectorMetadata.ts - Assign appropriate
contexts - Optionally provide a
descriptionandsmoketest: falseif it should be excluded from testing - No need to touch
selectors.tsor the smoketest logic — it’s all dynamic - Run
npm run smoketestand review output
🔍 Debugging Tips
- You can run the smoketest manually with:
npm run smoketest - If a selector isn’t being found:
- Open the live site in Chrome DevTools
- Paste your selector in
document.querySelector(...)to confirm - Consider adding
scrollIntoViewIfNeeded()or waiting for delayed DOM load