feat(web): translate location, travel, and world-map screen copy to English
This commit is contained in:
@@ -3,14 +3,14 @@
|
|||||||
class="interaction-panel"
|
class="interaction-panel"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="false"
|
aria-modal="false"
|
||||||
[attr.aria-label]="result ? result.title : 'Handlung nicht möglich'"
|
[attr.aria-label]="result ? result.title : 'Action not possible'"
|
||||||
data-interaction-panel
|
data-interaction-panel
|
||||||
>
|
>
|
||||||
@if (result) {
|
@if (result) {
|
||||||
<h2 class="interaction-panel__title">{{ result.title }}</h2>
|
<h2 class="interaction-panel__title">{{ result.title }}</h2>
|
||||||
<p class="interaction-panel__text">{{ result.text }}</p>
|
<p class="interaction-panel__text">{{ result.text }}</p>
|
||||||
} @else {
|
} @else {
|
||||||
<h2 class="interaction-panel__title">Nichts zu entdecken</h2>
|
<h2 class="interaction-panel__title">Nothing to discover</h2>
|
||||||
<p class="interaction-panel__text interaction-panel__text--error" role="alert">
|
<p class="interaction-panel__text interaction-panel__text--error" role="alert">
|
||||||
{{ error }}
|
{{ error }}
|
||||||
</p>
|
</p>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
data-interaction-close
|
data-interaction-close
|
||||||
(click)="closePanel.emit()"
|
(click)="closePanel.emit()"
|
||||||
>
|
>
|
||||||
Schließen
|
Close
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,6 @@
|
|||||||
<span class="location-node__marker" aria-hidden="true"></span>
|
<span class="location-node__marker" aria-hidden="true"></span>
|
||||||
<span class="location-node__name">{{ location.name }}</span>
|
<span class="location-node__name">{{ location.name }}</span>
|
||||||
<span class="location-node__state">{{
|
<span class="location-node__state">{{
|
||||||
current ? 'Aktueller Ort' : selected ? 'Ausgewähltes Ziel' : 'Erreichbar'
|
current ? 'Current Location' : selected ? 'Selected Destination' : 'Reachable'
|
||||||
}}</span>
|
}}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<section class="location-page" aria-label="Ortsansicht">
|
<section class="location-page" aria-label="Location View">
|
||||||
@if (store.location(); as location) {
|
@if (store.location(); as location) {
|
||||||
<div class="location-page__main">
|
<div class="location-page__main">
|
||||||
<header class="location-page__header">
|
<header class="location-page__header">
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
@if (runtimeArtwork(location.localArtworkPath); as runtime) {
|
@if (runtimeArtwork(location.localArtworkPath); as runtime) {
|
||||||
<source [srcset]="runtime" type="image/jpeg" />
|
<source [srcset]="runtime" type="image/jpeg" />
|
||||||
}
|
}
|
||||||
<img [src]="location.localArtworkPath" [alt]="'Ortsansicht: ' + location.name" />
|
<img [src]="location.localArtworkPath" [alt]="'Location view: ' + location.name" />
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
<div class="location-page__hotspots">
|
<div class="location-page__hotspots">
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="location-page__actions" aria-label="Ortsaktionen">
|
<nav class="location-page__actions" aria-label="Location Actions">
|
||||||
@for (action of location.primaryActions; track action.key) {
|
@for (action of location.primaryActions; track action.key) {
|
||||||
<button
|
<button
|
||||||
class="location-action"
|
class="location-action"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<span class="location-action__label">{{ action.label }}</span>
|
<span class="location-action__label">{{ action.label }}</span>
|
||||||
@if (action.description) {
|
@if (action.description) {
|
||||||
<span class="location-action__hint">
|
<span class="location-action__hint">
|
||||||
{{ isPending(action.poiKey ?? action.key) ? 'Einen Moment…' : action.description }}
|
{{ isPending(action.poiKey ?? action.key) ? 'One moment…' : action.description }}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
@@ -60,13 +60,13 @@
|
|||||||
<app-location-sidebar class="location-page__sidebar" [location]="location" />
|
<app-location-sidebar class="location-page__sidebar" [location]="location" />
|
||||||
} @else if (store.error(); as error) {
|
} @else if (store.error(); as error) {
|
||||||
<section class="location-page__notice" role="alert">
|
<section class="location-page__notice" role="alert">
|
||||||
<p>Ort konnte nicht geladen werden.</p>
|
<p>Could not load location.</p>
|
||||||
<p class="location-page__notice-detail">{{ error }}</p>
|
<p class="location-page__notice-detail">{{ error }}</p>
|
||||||
<button type="button" data-location-retry (click)="retry()">Erneut versuchen</button>
|
<button type="button" data-location-retry (click)="retry()">Retry</button>
|
||||||
</section>
|
</section>
|
||||||
} @else {
|
} @else {
|
||||||
<section class="location-page__notice" aria-live="polite">
|
<section class="location-page__notice" aria-live="polite">
|
||||||
<p role="status">Der Ort wird geladen…</p>
|
<p role="status">Loading location…</p>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ describe('LocationPageComponent', () => {
|
|||||||
const image = element.querySelector('img') as HTMLImageElement;
|
const image = element.querySelector('img') as HTMLImageElement;
|
||||||
|
|
||||||
expect(image.getAttribute('src')).toBe('/images/backgrounds/Aschestrasse.png');
|
expect(image.getAttribute('src')).toBe('/images/backgrounds/Aschestrasse.png');
|
||||||
expect(image.getAttribute('alt')).toBe('Ortsansicht: Verbrannte Straße');
|
expect(image.getAttribute('alt')).toBe('Location view: Verbrannte Straße');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('places every hotspot on the artwork', async () => {
|
it('places every hotspot on the artwork', async () => {
|
||||||
@@ -205,14 +205,14 @@ describe('LocationPageComponent', () => {
|
|||||||
|
|
||||||
const actions = element.querySelectorAll<HTMLButtonElement>('[data-action]');
|
const actions = element.querySelectorAll<HTMLButtonElement>('[data-action]');
|
||||||
expect([...actions].every((action) => action.disabled)).toBe(true);
|
expect([...actions].every((action) => action.disabled)).toBe(true);
|
||||||
expect(element.textContent).toContain('Einen Moment…');
|
expect(element.textContent).toContain('One moment…');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a restrained loading state that shows no misplaced hotspots', async () => {
|
it('renders a restrained loading state that shows no misplaced hotspots', async () => {
|
||||||
const { element } = await setup(null);
|
const { element } = await setup(null);
|
||||||
|
|
||||||
expect(element.querySelector('[role="status"]')?.textContent).toContain(
|
expect(element.querySelector('[role="status"]')?.textContent).toContain(
|
||||||
'Der Ort wird geladen',
|
'Loading location',
|
||||||
);
|
);
|
||||||
expect(element.querySelectorAll('app-location-poi')).toHaveLength(0);
|
expect(element.querySelectorAll('app-location-poi')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
@@ -224,7 +224,7 @@ describe('LocationPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(element.querySelector('[role="alert"]')?.textContent).toContain(
|
expect(element.querySelector('[role="alert"]')?.textContent).toContain(
|
||||||
'Ort konnte nicht geladen werden.',
|
'Could not load location.',
|
||||||
);
|
);
|
||||||
|
|
||||||
element.querySelector<HTMLButtonElement>('[data-location-retry]')?.click();
|
element.querySelector<HTMLButtonElement>('[data-location-retry]')?.click();
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
<aside class="location-sidebar" aria-label="Ortsinformationen">
|
<aside class="location-sidebar" aria-label="Location Information">
|
||||||
<dl class="location-sidebar__identity">
|
<dl class="location-sidebar__identity">
|
||||||
<div>
|
<div>
|
||||||
<dt>Gebiet</dt>
|
<dt>Region</dt>
|
||||||
<dd>{{ location.regionName }}</dd>
|
<dd>{{ location.regionName }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Ort</dt>
|
<dt>Location</dt>
|
||||||
<dd class="location-sidebar__name">{{ location.name }}</dd>
|
<dd class="location-sidebar__name">{{ location.name }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Ortstyp</dt>
|
<dt>Location Type</dt>
|
||||||
<dd>{{ locationTypeLabel }}</dd>
|
<dd>{{ locationTypeLabel }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Empfohlenes Ansehen</dt>
|
<dt>Recommended Renown</dt>
|
||||||
<dd class="location-sidebar__recommendation">{{ location.recommendationLabel }}</dd>
|
<dd class="location-sidebar__recommendation">{{ location.recommendationLabel }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Gefahr</dt>
|
<dt>Danger</dt>
|
||||||
<dd>
|
<dd>
|
||||||
@if (location.dangerRating; as rating) {
|
@if (location.dangerRating; as rating) {
|
||||||
<app-danger-badge [rating]="rating" />
|
<app-danger-badge [rating]="rating" />
|
||||||
} @else {
|
} @else {
|
||||||
<span class="location-sidebar__safe">Sicher</span>
|
<span class="location-sidebar__safe">Safe</span>
|
||||||
}
|
}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
@if (location.encounterPreview.length) {
|
@if (location.encounterPreview.length) {
|
||||||
<section class="location-sidebar__block" data-sidebar="encounters">
|
<section class="location-sidebar__block" data-sidebar="encounters">
|
||||||
<h3>Mögliche Begegnungen</h3>
|
<h3>Possible Encounters</h3>
|
||||||
<ul class="location-sidebar__encounters">
|
<ul class="location-sidebar__encounters">
|
||||||
@for (encounter of location.encounterPreview; track encounter.key) {
|
@for (encounter of location.encounterPreview; track encounter.key) {
|
||||||
<li>
|
<li>
|
||||||
@@ -39,13 +39,13 @@
|
|||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
<p class="location-sidebar__note">Vorschau — die Jagd würfelt eigenständig.</p>
|
<p class="location-sidebar__note">Preview — the hunt rolls independently.</p>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (interactions.length) {
|
@if (interactions.length) {
|
||||||
<section class="location-sidebar__block" data-sidebar="interactions">
|
<section class="location-sidebar__block" data-sidebar="interactions">
|
||||||
<h3>Verfügbare Interaktionen</h3>
|
<h3>Available Interactions</h3>
|
||||||
<ul class="location-sidebar__interactions">
|
<ul class="location-sidebar__interactions">
|
||||||
@for (interaction of interactions; track interaction.label) {
|
@for (interaction of interactions; track interaction.label) {
|
||||||
<li>
|
<li>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
|
|
||||||
@if (location.rewardPreview.length) {
|
@if (location.rewardPreview.length) {
|
||||||
<section class="location-sidebar__block" data-sidebar="rewards">
|
<section class="location-sidebar__block" data-sidebar="rewards">
|
||||||
<h3>Mögliche Belohnungen</h3>
|
<h3>Possible Rewards</h3>
|
||||||
<ul class="location-sidebar__rewards">
|
<ul class="location-sidebar__rewards">
|
||||||
@for (reward of location.rewardPreview; track reward.key) {
|
@for (reward of location.rewardPreview; track reward.key) {
|
||||||
<li [attr.title]="reward.label">
|
<li [attr.title]="reward.label">
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe('LocationSidebarComponent', () => {
|
|||||||
const element = await render(southGateFixture());
|
const element = await render(southGateFixture());
|
||||||
|
|
||||||
expect(element.querySelector('app-danger-badge')).toBeNull();
|
expect(element.querySelector('app-danger-badge')).toBeNull();
|
||||||
expect(element.textContent).toContain('Sicher');
|
expect(element.textContent).toContain('Safe');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('previews the encounters and marks them as preview only', async () => {
|
it('previews the encounters and marks them as preview only', async () => {
|
||||||
@@ -47,7 +47,7 @@ describe('LocationSidebarComponent', () => {
|
|||||||
'Aschenratte',
|
'Aschenratte',
|
||||||
'Straßenräuber',
|
'Straßenräuber',
|
||||||
]);
|
]);
|
||||||
expect(element.textContent).toContain('die Jagd würfelt eigenständig');
|
expect(element.textContent).toContain('the hunt rolls independently');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('lists each interaction kind once, taken from the enabled hotspots', async () => {
|
it('lists each interaction kind once, taken from the enabled hotspots', async () => {
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
<section class="travel-panel" aria-labelledby="travel-panel-title">
|
<section class="travel-panel" aria-labelledby="travel-panel-title">
|
||||||
@if (travellingTravel; as travel) {
|
@if (travellingTravel; as travel) {
|
||||||
<span class="travel-panel__eyebrow" role="status">REISE LÄUFT</span>
|
<span class="travel-panel__eyebrow" role="status">TRAVELING</span>
|
||||||
<h2 id="travel-panel-title">Reiseziel: {{ travel.targetLocation.name }}</h2>
|
<h2 id="travel-panel-title">Destination: {{ travel.targetLocation.name }}</h2>
|
||||||
<dl class="travel-panel__details">
|
<dl class="travel-panel__details">
|
||||||
<div>
|
<div>
|
||||||
<dt>Ankunft in</dt>
|
<dt>Arrival in</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<time role="timer">{{ formatDuration(remainingSeconds ?? 0) }}</time>
|
<time role="timer">{{ formatDuration(remainingSeconds ?? 0) }}</time>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Ziel</dt>
|
<dt>Destination</dt>
|
||||||
<dd>{{ travel.targetLocation.name }}</dd>
|
<dd>{{ travel.targetLocation.name }}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<button type="button" data-travel-start disabled>Reise läuft</button>
|
<button type="button" data-travel-start disabled>Travel in progress</button>
|
||||||
} @else if (selectedConnection; as connection) {
|
} @else if (selectedConnection; as connection) {
|
||||||
<span class="travel-panel__eyebrow">REISEN</span>
|
<span class="travel-panel__eyebrow">TRAVEL</span>
|
||||||
<h2 id="travel-panel-title">Zur {{ connection.targetLocation.name }} reisen?</h2>
|
<h2 id="travel-panel-title">Travel to {{ connection.targetLocation.name }}?</h2>
|
||||||
<dl class="travel-panel__details">
|
<dl class="travel-panel__details">
|
||||||
<div>
|
<div>
|
||||||
<dt>Ziel</dt>
|
<dt>Destination</dt>
|
||||||
<dd>{{ connection.targetLocation.name }}</dd>
|
<dd>{{ connection.targetLocation.name }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Reisezeit</dt>
|
<dt>Travel Time</dt>
|
||||||
<dd>{{ formatDuration(connection.travelDurationSeconds) }}</dd>
|
<dd>{{ formatDuration(connection.travelDurationSeconds) }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>Gefahr</dt>
|
<dt>Danger</dt>
|
||||||
<dd [class.travel-panel__danger--low]="connection.danger === 'LOW'">
|
<dd [class.travel-panel__danger--low]="connection.danger === 'LOW'">
|
||||||
{{ dangerLabel(connection.danger) }}
|
{{ dangerLabel(connection.danger) }}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<button type="button" data-travel-start [disabled]="busy" (click)="travelStart.emit()">
|
<button type="button" data-travel-start [disabled]="busy" (click)="travelStart.emit()">
|
||||||
Reise beginnen
|
Begin Travel
|
||||||
</button>
|
</button>
|
||||||
} @else {
|
} @else {
|
||||||
<span class="travel-panel__eyebrow">REISEN</span>
|
<span class="travel-panel__eyebrow">TRAVEL</span>
|
||||||
<p id="travel-panel-title" class="travel-panel__instruction">
|
<p id="travel-panel-title" class="travel-panel__instruction">
|
||||||
Wähle einen erreichbaren Ort auf der Karte.
|
Select a reachable location on the map.
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -27,6 +27,6 @@ export class TravelPanelComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected dangerLabel(danger: CurrentLocationConnection['danger']): string {
|
protected dangerLabel(danger: CurrentLocationConnection['danger']): string {
|
||||||
return danger === 'LOW' ? 'Niedrig' : 'Hoch';
|
return danger === 'LOW' ? 'Low' : 'High';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<section class="world-page" aria-label="Weltansicht">
|
<section class="world-page" aria-label="World View">
|
||||||
@if (worldStore.currentLocation(); as location) {
|
@if (worldStore.currentLocation(); as location) {
|
||||||
<section class="world-page__scene" [attr.aria-label]="'Weltkarte bei ' + location.name">
|
<section class="world-page__scene" [attr.aria-label]="'World map at ' + location.name">
|
||||||
<div class="world-page__atmosphere" aria-hidden="true"></div>
|
<div class="world-page__atmosphere" aria-hidden="true"></div>
|
||||||
<p class="world-page__location-title">{{ location.name }}</p>
|
<p class="world-page__location-title">{{ location.name }}</p>
|
||||||
|
|
||||||
@@ -43,18 +43,18 @@
|
|||||||
/>
|
/>
|
||||||
} @else {
|
} @else {
|
||||||
<section class="world-page__empty" aria-live="polite">
|
<section class="world-page__empty" aria-live="polite">
|
||||||
<p>Weltkarte wird vorbereitet.</p>
|
<p>Preparing world map.</p>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (worldStore.loading()) {
|
@if (worldStore.loading()) {
|
||||||
<p class="world-page__loading" role="status">Weltzustand wird geladen…</p>
|
<p class="world-page__loading" role="status">Loading world state…</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (worldStore.error(); as error) {
|
@if (worldStore.error(); as error) {
|
||||||
<section class="world-page__error" role="alert">
|
<section class="world-page__error" role="alert">
|
||||||
<p>{{ error }}</p>
|
<p>{{ error }}</p>
|
||||||
<button type="button" data-world-retry (click)="retry()">Erneut versuchen</button>
|
<button type="button" data-world-retry (click)="retry()">Retry</button>
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -83,10 +83,10 @@ describe('WorldPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(store.selectConnection).toHaveBeenCalledWith(burnedRoadConnection);
|
expect(store.selectConnection).toHaveBeenCalledWith(burnedRoadConnection);
|
||||||
expect(element.textContent).toContain('Ziel');
|
expect(element.textContent).toContain('Destination');
|
||||||
expect(element.textContent).toContain('Verbrannte Straße');
|
expect(element.textContent).toContain('Verbrannte Straße');
|
||||||
expect(element.textContent).toContain('00:00:10');
|
expect(element.textContent).toContain('00:00:10');
|
||||||
expect(element.textContent).toContain('Niedrig');
|
expect(element.textContent).toContain('Low');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps each location at its geographic position when the authoritative current location swaps', () => {
|
it('keeps each location at its geographic position when the authoritative current location swaps', () => {
|
||||||
@@ -140,8 +140,8 @@ describe('WorldPageComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const element = fixture.nativeElement as HTMLElement;
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
expect(element.textContent).toContain('Reiseziel: Verbrannte Straße');
|
expect(element.textContent).toContain('Destination: Verbrannte Straße');
|
||||||
expect(element.textContent).toContain('Ankunft in');
|
expect(element.textContent).toContain('Arrival in');
|
||||||
expect(element.textContent).toContain('00:00:06');
|
expect(element.textContent).toContain('00:00:06');
|
||||||
expect(element.querySelector<HTMLButtonElement>('[data-travel-start]')?.disabled).toBe(true);
|
expect(element.querySelector<HTMLButtonElement>('[data-travel-start]')?.disabled).toBe(true);
|
||||||
expect(element.querySelector('app-travel-panel section')?.hasAttribute('aria-live')).toBe(
|
expect(element.querySelector('app-travel-panel section')?.hasAttribute('aria-live')).toBe(
|
||||||
@@ -170,13 +170,13 @@ describe('WorldPageComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('retries a displayed world error through the store', () => {
|
it('retries a displayed world error through the store', () => {
|
||||||
store.error.set('Weltzustand nicht verfügbar.');
|
store.error.set('World state unavailable.');
|
||||||
const fixture = TestBed.createComponent(WorldPageComponent);
|
const fixture = TestBed.createComponent(WorldPageComponent);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const element = fixture.nativeElement as HTMLElement;
|
const element = fixture.nativeElement as HTMLElement;
|
||||||
expect(element.querySelector('[role="alert"]')?.textContent).toContain(
|
expect(element.querySelector('[role="alert"]')?.textContent).toContain(
|
||||||
'Weltzustand nicht verfügbar.',
|
'World state unavailable.',
|
||||||
);
|
);
|
||||||
element.querySelector<HTMLButtonElement>('[data-world-retry]')?.click();
|
element.querySelector<HTMLButtonElement>('[data-world-retry]')?.click();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user