-
-
Notifications
You must be signed in to change notification settings - Fork 34
feat: expose window.__tolgee API for Playwright and console #3499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Adds a window.__tolgee object when InContextTools plugin is active, enabling programmatic access to visible translation keys and their positions from Playwright tests or the browser console. This allows automated screenshot tools to discover which keys are visible on screen and where they are, making it possible to associate screenshots with translation keys in the Tolgee platform. API: - getVisibleKeys() - returns keys with bounding rects (viewport-filtered) - highlight(keyName?, ns?) - highlights matching DOM elements - isRunning() - checks if the tolgee observer is active
WalkthroughThis PR introduces a Window API integration for Tolgee that exposes key translation utilities (getVisibleKeys, highlight, isRunning) on the global Changes
Sequence DiagramsequenceDiagram
participant Client as Browser Client
participant InContextTools
participant WindowApi
participant TolgeeInstance as Tolgee Instance
participant DOM
Client->>InContextTools: Initialize Tolgee
InContextTools->>WindowApi: setupWindowApi(tolgee)
WindowApi->>WindowApi: Check SSR environment
WindowApi->>DOM: Attach window.__tolgee API
WindowApi-->>InContextTools: Return cleanup function
InContextTools->>TolgeeInstance: Subscribe to 'running' event
Note over Client,DOM: User interaction
TolgeeInstance->>InContextTools: Emit 'running' event (state change)
InContextTools->>WindowApi: Call teardownWindowApi()
WindowApi->>DOM: Remove window.__tolgee
alt Running state is true
InContextTools->>WindowApi: setupWindowApi(tolgee)
WindowApi->>DOM: Re-attach window.__tolgee API
WindowApi-->>InContextTools: Return cleanup function
end
Note over Client,DOM: API Usage
Client->>DOM: Call window.__tolgee.getVisibleKeys()
DOM->>TolgeeInstance: Filter positions by viewport
DOM-->>Client: Return visible key positions
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/web/src/package/__test__/windowApi.test.ts`:
- Around line 43-73: The test title says getVisibleKeys() but the body asserts
tolgee.findPositions(); update the test so the behavior matches the name by
calling the window API (window.__tolgee.getVisibleKeys()) after rendering and
awaiting the attribute, then assert the returned array has length > 0 and that
its first element matches { keyName: 'hello', keyNamespace: '' } and has a
defined position; alternatively, if you prefer to keep the existing assertions
on tolgee.findPositions(), simply rename the test description string to reflect
findPositions() instead of getVisibleKeys().
In `@packages/web/src/package/WindowApi.ts`:
- Around line 16-39: The teardown returned by setupWindowApi unconditionally
deletes window.__tolgee which can remove another Tolgee instance’s API; capture
the reference assigned (e.g., const api = { ... } assigned to window.__tolgee)
and in the returned cleanup function only delete window.__tolgee if
window.__tolgee === api, otherwise leave it intact; update setupWindowApi to
create the api object, assign it to window.__tolgee, and perform the guarded
delete in the returned function, referencing the setupWindowApi, window.__tolgee
and the local api variable names to locate the change.
| it('getVisibleKeys() returns key positions for rendered translations', async () => { | ||
| tolgee = TolgeeCore() | ||
| .use(InContextTools()) | ||
| .init({ | ||
| language: 'en', | ||
| staticData: { en: { hello: 'world' } }, | ||
| observerType, | ||
| }); | ||
| await tolgee.run(); | ||
|
|
||
| document.body.innerHTML = ` | ||
| <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span> | ||
| `; | ||
|
|
||
| await waitFor(() => { | ||
| expect( | ||
| document | ||
| .querySelector('[data-testid="translation"]') | ||
| ?.getAttribute(TOLGEE_ATTRIBUTE_NAME) | ||
| ).not.toBeFalsy(); | ||
| }); | ||
|
|
||
| // findPositions finds the key in the DOM (jsdom returns zero-size rects) | ||
| const allPositions = tolgee.findPositions(); | ||
| expect(allPositions.length).toBeGreaterThan(0); | ||
| expect(allPositions[0]).toMatchObject({ | ||
| keyName: 'hello', | ||
| keyNamespace: '', | ||
| }); | ||
| expect(allPositions[0].position).toBeDefined(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test name doesn’t match assertions.
Line 43’s test never calls window.__tolgee.getVisibleKeys(); it validates tolgee.findPositions() instead. Either rename the test or add a call to the window API so the title matches the behavior.
✏️ Rename option
-it('getVisibleKeys() returns key positions for rendered translations', async () => {
+it('findPositions() returns key positions for rendered translations', async () => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('getVisibleKeys() returns key positions for rendered translations', async () => { | |
| tolgee = TolgeeCore() | |
| .use(InContextTools()) | |
| .init({ | |
| language: 'en', | |
| staticData: { en: { hello: 'world' } }, | |
| observerType, | |
| }); | |
| await tolgee.run(); | |
| document.body.innerHTML = ` | |
| <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span> | |
| `; | |
| await waitFor(() => { | |
| expect( | |
| document | |
| .querySelector('[data-testid="translation"]') | |
| ?.getAttribute(TOLGEE_ATTRIBUTE_NAME) | |
| ).not.toBeFalsy(); | |
| }); | |
| // findPositions finds the key in the DOM (jsdom returns zero-size rects) | |
| const allPositions = tolgee.findPositions(); | |
| expect(allPositions.length).toBeGreaterThan(0); | |
| expect(allPositions[0]).toMatchObject({ | |
| keyName: 'hello', | |
| keyNamespace: '', | |
| }); | |
| expect(allPositions[0].position).toBeDefined(); | |
| }); | |
| it('findPositions() returns key positions for rendered translations', async () => { | |
| tolgee = TolgeeCore() | |
| .use(InContextTools()) | |
| .init({ | |
| language: 'en', | |
| staticData: { en: { hello: 'world' } }, | |
| observerType, | |
| }); | |
| await tolgee.run(); | |
| document.body.innerHTML = ` | |
| <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span> | |
| `; | |
| await waitFor(() => { | |
| expect( | |
| document | |
| .querySelector('[data-testid="translation"]') | |
| ?.getAttribute(TOLGEE_ATTRIBUTE_NAME) | |
| ).not.toBeFalsy(); | |
| }); | |
| // findPositions finds the key in the DOM (jsdom returns zero-size rects) | |
| const allPositions = tolgee.findPositions(); | |
| expect(allPositions.length).toBeGreaterThan(0); | |
| expect(allPositions[0]).toMatchObject({ | |
| keyName: 'hello', | |
| keyNamespace: '', | |
| }); | |
| expect(allPositions[0].position).toBeDefined(); | |
| }); |
🧰 Tools
🪛 ast-grep (0.40.5)
[warning] 52-54: Direct modification of innerHTML or outerHTML properties detected. Modifying these properties with unsanitized user input can lead to XSS vulnerabilities. Use safe alternatives or sanitize content first.
Context: document.body.innerHTML = <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://owasp.org/www-community/xss-filter-evasion-cheatsheet
- https://cwe.mitre.org/data/definitions/79.html
(dom-content-modification)
[warning] 52-54: Direct HTML content assignment detected. Modifying innerHTML, outerHTML, or using document.write with unsanitized content can lead to XSS vulnerabilities. Use secure alternatives like textContent or sanitize HTML with libraries like DOMPurify.
Context: document.body.innerHTML = <span data-testid="translation">${tolgee.t({ key: 'hello' })}</span>
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://www.dhairyashah.dev/posts/why-innerhtml-is-a-bad-idea-and-how-to-avoid-it/
- https://cwe.mitre.org/data/definitions/79.html
(unsafe-html-content-assignment)
🤖 Prompt for AI Agents
In `@packages/web/src/package/__test__/windowApi.test.ts` around lines 43 - 73,
The test title says getVisibleKeys() but the body asserts
tolgee.findPositions(); update the test so the behavior matches the name by
calling the window API (window.__tolgee.getVisibleKeys()) after rendering and
awaiting the attribute, then assert the returned array has length > 0 and that
its first element matches { keyName: 'hello', keyNamespace: '' } and has a
defined position; alternatively, if you prefer to keep the existing assertions
on tolgee.findPositions(), simply rename the test description string to reflect
findPositions() instead of getVisibleKeys().
| export function setupWindowApi(tolgee: TolgeeInstance): () => void { | ||
| if (isSSR()) { | ||
| return () => {}; | ||
| } | ||
|
|
||
| window.__tolgee = { | ||
| getVisibleKeys() { | ||
| const vw = window.innerWidth; | ||
| const vh = window.innerHeight; | ||
| return tolgee.findPositions().filter(({ position: p }) => { | ||
| return p.x + p.width > 0 && p.y + p.height > 0 && p.x < vw && p.y < vh; | ||
| }); | ||
| }, | ||
| highlight(keyName?: string, ns?: string) { | ||
| return tolgee.highlight(keyName, ns); | ||
| }, | ||
| isRunning() { | ||
| return tolgee.isRunning(); | ||
| }, | ||
| }; | ||
|
|
||
| return () => { | ||
| delete window.__tolgee; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard cleanup to avoid deleting another instance’s API.
If multiple Tolgee instances exist, teardown currently deletes window.__tolgee even if another instance overwrote it. Consider deleting only when the stored reference matches.
🛠️ Proposed fix
- window.__tolgee = {
+ const api = {
getVisibleKeys() {
const vw = window.innerWidth;
const vh = window.innerHeight;
return tolgee.findPositions().filter(({ position: p }) => {
return p.x + p.width > 0 && p.y + p.height > 0 && p.x < vw && p.y < vh;
});
},
highlight(keyName?: string, ns?: string) {
return tolgee.highlight(keyName, ns);
},
isRunning() {
return tolgee.isRunning();
},
};
+ window.__tolgee = api;
return () => {
- delete window.__tolgee;
+ if (window.__tolgee === api) {
+ delete window.__tolgee;
+ }
};🤖 Prompt for AI Agents
In `@packages/web/src/package/WindowApi.ts` around lines 16 - 39, The teardown
returned by setupWindowApi unconditionally deletes window.__tolgee which can
remove another Tolgee instance’s API; capture the reference assigned (e.g.,
const api = { ... } assigned to window.__tolgee) and in the returned cleanup
function only delete window.__tolgee if window.__tolgee === api, otherwise leave
it intact; update setupWindowApi to create the api object, assign it to
window.__tolgee, and perform the guarded delete in the returned function,
referencing the setupWindowApi, window.__tolgee and the local api variable names
to locate the change.
| return tolgee.highlight(keyName, ns); | ||
| }, | ||
| isRunning() { | ||
| return tolgee.isRunning(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a nit: with the current setup, it looks like this will always return true or won't exist.
Problem
When taking screenshots with Playwright, there is no way to programmatically discover which translation keys are visible on screen and where they are positioned. This makes it impossible to automatically associate screenshots with translation keys in the Tolgee platform — users have to manually tag each screenshot with the relevant keys.
Solution
Exposes a
window.__tolgeeobject (automatically set whenInContextTools/DevToolsplugin is active) with methods to query visible keys and their positions:getVisibleKeys()— returns all keys visible in the current viewport with their bounding rectshighlight(keyName?, ns?)— highlights matching DOM elements (returns an unhighlight handle)isRunning()— checks if the tolgee observer is activeUsage from Playwright
Test plan
getVisibleKeys()returns correct keys with positions, viewport filtering works (9 keys full viewport → 6 keys with 400px viewport)Summary by CodeRabbit
Documentation
New Features
Tests