feat: implementar pestañas de información e historial en la ficha del puesto de trabajo
This commit is contained in:
parent
e7ecf1558e
commit
0ceeb9658c
@ -1518,3 +1518,12 @@ software y perfil. `ApplicationResourceUsage`, compuesto desde Companies, consul
|
|||||||
a demanda las APIs operativas existentes de la compañía y conserva sus query
|
a demanda las APIs operativas existentes de la compañía y conserva sus query
|
||||||
keys. Resources no depende de Companies. Véanse contratos, evidencia y límites en
|
keys. Resources no depende de Companies. Véanse contratos, evidencia y límites en
|
||||||
[R2.6](architecture/resources-global-scope/17-r2-6-coexistencia-duplicados.md).
|
[R2.6](architecture/resources-global-scope/17-r2-6-coexistencia-duplicados.md).
|
||||||
|
|
||||||
|
### Pestañas de información e historial del puesto
|
||||||
|
|
||||||
|
La ficha actual conserva sus paneles en Información e integra `WorkstationHistory`
|
||||||
|
en Historial, con `Tabs`, `TabsContent` y `ScrollableTabsList`, siguiendo el contrato
|
||||||
|
genérico descrito arriba. La cabecera común mantiene identidad y acciones de edición.
|
||||||
|
Información permanece montada para conservar las configuraciones abiertas; el acceso
|
||||||
|
«Ver historial completo» activa Historial y sitúa el foco en su pestaña. La composición
|
||||||
|
se detalla en [la implementación del dashboard](workplace-dashboard-implementacion.md#pestañas-de-la-ficha-del-puesto).
|
||||||
|
|||||||
@ -1001,3 +1001,29 @@ No se modificaron vendor ni los ocho documentos históricos de auditoría.
|
|||||||
El preparador autenticado y sus servicios temporales se cerraron al terminar; el archivo
|
El preparador autenticado y sus servicios temporales se cerraron al terminar; el archivo
|
||||||
de sesión se retiró. Se conservaron únicamente las evidencias sanitizadas en la ruta
|
de sesión se retiró. Se conservaron únicamente las evidencias sanitizadas en la ruta
|
||||||
estable indicada. No se instalaron dependencias ni se cambió configuración global.
|
estable indicada. No se instalaron dependencias ni se cambió configuración global.
|
||||||
|
|
||||||
|
## Pestañas de la ficha del puesto
|
||||||
|
|
||||||
|
La ficha mantiene la identidad y las acciones del puesto en una cabecera común y
|
||||||
|
organiza el contenido mediante `Tabs` y `ScrollableTabsList`, siguiendo la ficha de activos:
|
||||||
|
|
||||||
|
- **Información** es la pestaña inicial y conserva los paneles actuales. Su contenido
|
||||||
|
permanece montado al alternar para mantener el contexto de las configuraciones abiertas.
|
||||||
|
- **Historial** integra `WorkstationHistory` con revisiones de más reciente a más antigua,
|
||||||
|
autor y valores de cada momento. No abre un diálogo desde esta ficha.
|
||||||
|
|
||||||
|
La cabecera ya no incluye el botón «Ver historial». «Ver historial completo», en el
|
||||||
|
resumen del último cambio, selecciona Historial y lleva el foco a su pestaña.
|
||||||
|
El diálogo de historial sigue disponible en el listado de puestos.
|
||||||
|
|
||||||
|
Validación de este cambio: typecheck de `@metis/companies`, Biome sobre el detalle,
|
||||||
|
ESLint sobre los tres ficheros TypeScript modificados y las 26 pruebas de
|
||||||
|
`workstation-detail.test.tsx`, correctos. La prueba MCP
|
||||||
|
`WorkstationDashboardTests.test_03_history_edit_software_and_navigation` pasó a
|
||||||
|
1440×900, 390×844 y 320×640: pestañas con flechas y Enter, foco desde el resumen,
|
||||||
|
conservación de configuración desplegada, ausencia de diálogo de historial,
|
||||||
|
sin desbordamiento horizontal del historial y regresión de edición/software/navegación.
|
||||||
|
Se inspeccionaron las seis capturas de ambas pestañas. Las fixtures desechables
|
||||||
|
contenían 15 compañías y, en la principal, 59 personas, 135 activos, 73 recursos
|
||||||
|
y 64 perfiles. No se repitieron las suites globales ni una auditoría completa
|
||||||
|
de estados de red o accesibilidad.
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export function useWorkstationDetailController(company: CompanyDTO, workstationI
|
|||||||
enabled: Boolean(computer),
|
enabled: Boolean(computer),
|
||||||
});
|
});
|
||||||
const configurationQueries = useWorkstationConfigurationQueries(company.id, Boolean(computer));
|
const configurationQueries = useWorkstationConfigurationQueries(company.id, Boolean(computer));
|
||||||
const [viewingHistory, setViewingHistory] = useState(false);
|
const [activeTab, setActiveTab] = useState<"information" | "history">("information");
|
||||||
const dashboard = workstation
|
const dashboard = workstation
|
||||||
? workstationDashboard(
|
? workstationDashboard(
|
||||||
company,
|
company,
|
||||||
@ -83,8 +83,8 @@ export function useWorkstationDetailController(company: CompanyDTO, workstationI
|
|||||||
selectedProfile,
|
selectedProfile,
|
||||||
expandedProfile,
|
expandedProfile,
|
||||||
profileExpansion,
|
profileExpansion,
|
||||||
viewingHistory,
|
activeTab,
|
||||||
setViewingHistory,
|
setActiveTab,
|
||||||
pendingCount,
|
pendingCount,
|
||||||
creatingProfile,
|
creatingProfile,
|
||||||
setCreatingProfile,
|
setCreatingProfile,
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
WorkstationActions,
|
WorkstationActions,
|
||||||
WorkstationEditorDialog,
|
WorkstationEditorDialog,
|
||||||
WorkstationHistoryDialog,
|
WorkstationHistory,
|
||||||
WorkstationNameEditor,
|
WorkstationNameEditor,
|
||||||
} from "@metis/assets/client";
|
} from "@metis/assets/client";
|
||||||
import { BrowserLink, returnToList, useBrowserSearchParams } from "@metis/core/client";
|
import { BrowserLink, returnToList, useBrowserSearchParams } from "@metis/core/client";
|
||||||
|
import { DetailPanel } from "@repo/rdx-ui/components/detail-panel";
|
||||||
import { EntityStatus } from "@repo/rdx-ui/components/entity-list";
|
import { EntityStatus } from "@repo/rdx-ui/components/entity-list";
|
||||||
import {
|
import { ScrollableTabsList } from "@repo/rdx-ui/components/scrollable-tabs-list";
|
||||||
Building2Icon,
|
import { ChevronLeftIcon, ClockIcon, MapPinIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
ChevronLeftIcon,
|
|
||||||
ClockIcon,
|
|
||||||
MapPinIcon,
|
|
||||||
PersonIcon,
|
|
||||||
} from "@repo/rdx-ui/lib/icons";
|
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
@ -21,12 +17,13 @@ import {
|
|||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
} from "@repo/shadcn-ui/components/breadcrumb";
|
} from "@repo/shadcn-ui/components/breadcrumb";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Tabs, TabsContent } from "@repo/shadcn-ui/components/tabs";
|
||||||
import type { ComponentProps, MouseEvent } from "react";
|
import type { ComponentProps, MouseEvent } from "react";
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { CompanyDTO } from "../../../../common";
|
import type { CompanyDTO } from "../../../../common";
|
||||||
|
import { PersonAvatar } from "../../../people/shared";
|
||||||
import { WorkProfileEditorDialog } from "../../../work-profiles/shared";
|
import { WorkProfileEditorDialog } from "../../../work-profiles/shared";
|
||||||
import { useWorkstationDetailController } from "../controllers/use-workstation-detail-controller";
|
import { useWorkstationDetailController } from "../controllers/use-workstation-detail-controller";
|
||||||
|
|
||||||
@ -44,13 +41,13 @@ export function WorkstationDetailPage({
|
|||||||
workstationId: string;
|
workstationId: string;
|
||||||
lookups: ComponentProps<typeof WorkstationEditorDialog>["lookups"];
|
lookups: ComponentProps<typeof WorkstationEditorDialog>["lookups"];
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation("assets");
|
const { t, i18n } = useTranslation("assets");
|
||||||
const c = useWorkstationDetailController(company, workstationId);
|
const c = useWorkstationDetailController(company, workstationId);
|
||||||
const editReturn = useRef<HTMLElement | null>(null);
|
const editReturn = useRef<HTMLElement | null>(null);
|
||||||
const editFromEmpty = useRef(false);
|
const editFromEmpty = useRef(false);
|
||||||
const editSaved = useRef(false);
|
const editSaved = useRef(false);
|
||||||
const headerEdit = useRef<HTMLButtonElement>(null);
|
const headerEdit = useRef<HTMLButtonElement>(null);
|
||||||
const historyReturn = useRef<HTMLElement | null>(null);
|
const tabsRef = useRef<HTMLDivElement>(null);
|
||||||
const { workstation, inventory, dashboard } = c;
|
const { workstation, inventory, dashboard } = c;
|
||||||
const root = `/companies/${company.id}`;
|
const root = `/companies/${company.id}`;
|
||||||
const { searchParams } = useBrowserSearchParams();
|
const { searchParams } = useBrowserSearchParams();
|
||||||
@ -73,9 +70,10 @@ export function WorkstationDetailPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onHistory = () => {
|
const onHistory = () => {
|
||||||
historyReturn.current =
|
c.setActiveTab("history");
|
||||||
document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
tabsRef.current
|
||||||
c.setViewingHistory(true);
|
?.querySelector<HTMLButtonElement>('[role="tab"][data-value="history"]')
|
||||||
|
?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -135,21 +133,12 @@ export function WorkstationDetailPage({
|
|||||||
onDeleted={() => returnToList(backHref)}
|
onDeleted={() => returnToList(backHref)}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
workstation={workstation}
|
workstation={workstation}
|
||||||
>
|
/>
|
||||||
<Button onClick={onHistory} variant="outline">
|
|
||||||
<ClockIcon aria-hidden="true" />
|
|
||||||
{t("workstationDashboard.viewHistory")}
|
|
||||||
</Button>
|
|
||||||
</WorkstationActions>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground @min-[640px]:gap-x-5 @min-[640px]:gap-y-2">
|
<div className="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground @min-[640px]:gap-x-5 @min-[640px]:gap-y-2">
|
||||||
<EntityStatus active={workstation.values.active}>
|
<EntityStatus active={workstation.values.active}>
|
||||||
{t(workstation.values.active ? "active" : "inactive")}
|
{t(workstation.values.active ? "active" : "inactive")}
|
||||||
</EntityStatus>
|
</EntityStatus>
|
||||||
<BrowserLink className="inline-flex min-w-0 items-center gap-1.5" href={root}>
|
|
||||||
<Building2Icon aria-hidden="true" className="size-4 shrink-0" />
|
|
||||||
<span className="break-words">{company.name}</span>
|
|
||||||
</BrowserLink>
|
|
||||||
<span className="inline-flex min-w-0 items-center gap-1.5">
|
<span className="inline-flex min-w-0 items-center gap-1.5">
|
||||||
<MapPinIcon aria-hidden="true" className="size-4 shrink-0" />
|
<MapPinIcon aria-hidden="true" className="size-4 shrink-0" />
|
||||||
<span className="sr-only">{t("site")}: </span>
|
<span className="sr-only">{t("site")}: </span>
|
||||||
@ -163,15 +152,22 @@ export function WorkstationDetailPage({
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="inline-flex min-w-0 flex-wrap items-center gap-1.5">
|
<span className="inline-flex min-w-0 flex-wrap items-center gap-1.5">
|
||||||
<PersonIcon aria-hidden="true" className="size-4 shrink-0" />
|
|
||||||
<span className="sr-only">{t("workstationDetail.assignedPerson")}: </span>
|
<span className="sr-only">{t("workstationDetail.assignedPerson")}: </span>
|
||||||
{responsible ? (
|
{responsible ? (
|
||||||
<>
|
<>
|
||||||
<BrowserLink
|
<BrowserLink
|
||||||
className="min-w-0 break-words [overflow-wrap:anywhere]"
|
className="inline-flex min-w-0 items-center gap-1.5"
|
||||||
href={`${root}/people/${responsible.id}`}
|
href={`${root}/people/${responsible.id}`}
|
||||||
>
|
>
|
||||||
|
<PersonAvatar
|
||||||
|
companyId={company.id}
|
||||||
|
name={responsible.name}
|
||||||
|
personId={responsible.id}
|
||||||
|
photoVersion={responsible.photoVersion}
|
||||||
|
/>
|
||||||
|
<span className="min-w-0 break-words [overflow-wrap:anywhere]">
|
||||||
{responsible.name}
|
{responsible.name}
|
||||||
|
</span>
|
||||||
</BrowserLink>
|
</BrowserLink>
|
||||||
<WorkstationResponsibleContext
|
<WorkstationResponsibleContext
|
||||||
company={company}
|
company={company}
|
||||||
@ -185,30 +181,59 @@ export function WorkstationDetailPage({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
<Tabs
|
||||||
|
className="min-w-0 gap-3"
|
||||||
|
key={workstation.id}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
if (value === "information" || value === "history") {
|
||||||
|
c.setActiveTab(value);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
ref={tabsRef}
|
||||||
|
value={c.activeTab}
|
||||||
|
>
|
||||||
|
<ScrollableTabsList
|
||||||
|
items={[
|
||||||
|
{ value: "information", label: t("workstationDetail.information") },
|
||||||
|
{ value: "history", label: t("history") },
|
||||||
|
]}
|
||||||
|
label={t("workstationDetail.sections")}
|
||||||
|
locale={i18n.language}
|
||||||
|
value={c.activeTab}
|
||||||
|
/>
|
||||||
|
<TabsContent className="min-w-0" keepMounted value="information">
|
||||||
<WorkstationDetailContent
|
<WorkstationDetailContent
|
||||||
company={company}
|
company={company}
|
||||||
controller={c}
|
controller={c}
|
||||||
key={workstation.id}
|
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
onHistory={onHistory}
|
onHistory={onHistory}
|
||||||
/>
|
/>
|
||||||
{c.viewingHistory && (
|
</TabsContent>
|
||||||
<WorkstationHistoryDialog
|
<TabsContent className="min-w-0" value="history">
|
||||||
finalFocus={() => historyReturn.current ?? true}
|
<DetailPanel
|
||||||
|
description={t("workstationDetail.historyHint")}
|
||||||
|
icon={<ClockIcon />}
|
||||||
|
title={t("workstationHistory")}
|
||||||
|
>
|
||||||
|
<WorkstationHistory
|
||||||
members={(ids) =>
|
members={(ids) =>
|
||||||
ids
|
ids
|
||||||
.map(
|
.map(
|
||||||
(id) =>
|
(id) =>
|
||||||
inventory.data?.assets.find((asset) => asset.id === id)?.values.name ?? "—"
|
inventory.data?.assets.find((asset) => asset.id === id)?.values.name ??
|
||||||
|
"—"
|
||||||
)
|
)
|
||||||
.join(", ") || "—"
|
.join(", ") || "—"
|
||||||
}
|
}
|
||||||
onClose={() => c.setViewingHistory(false)}
|
personName={(id) =>
|
||||||
personName={(id) => company.people.find((person) => person.id === id)?.name ?? "—"}
|
company.people.find((person) => person.id === id)?.name ?? "—"
|
||||||
|
}
|
||||||
siteName={(id) => company.sites.find((site) => site.id === id)?.name ?? "—"}
|
siteName={(id) => company.sites.find((site) => site.id === id)?.name ?? "—"}
|
||||||
workstation={workstation}
|
workstation={workstation}
|
||||||
/>
|
/>
|
||||||
)}
|
</DetailPanel>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
{c.creatingProfile && (
|
{c.creatingProfile && (
|
||||||
<WorkProfileEditorDialog
|
<WorkProfileEditorDialog
|
||||||
company={company}
|
company={company}
|
||||||
|
|||||||
@ -165,8 +165,13 @@ test("ficha de puesto es/en: cabecera, retorno, bloques vacíos y edición modal
|
|||||||
.map(([button]) => button)
|
.map(([button]) => button)
|
||||||
.filter((button) => button.includes('role="tab"'));
|
.filter((button) => button.includes('role="tab"'));
|
||||||
|
|
||||||
assert.equal(tabs.length, 0);
|
assert.equal(tabs.length, 2);
|
||||||
assert.ok(html.includes(labels.workstationDashboard.viewHistory));
|
assert.ok(tabs[0]?.includes(labels.workstationDetail.information));
|
||||||
|
assert.ok(tabs[0]?.includes('aria-selected="true"'));
|
||||||
|
assert.ok(tabs[1]?.includes(labels.history));
|
||||||
|
assert.ok(tabs[1]?.includes('aria-selected="false"'));
|
||||||
|
assert.equal(header.includes(labels.workstationDashboard.viewHistory), false);
|
||||||
|
assert.ok(html.includes(labels.workstationDashboard.fullHistory));
|
||||||
assert.ok(html.includes(labels.workstationDashboard.softwareTitle));
|
assert.ok(html.includes(labels.workstationDashboard.softwareTitle));
|
||||||
assert.equal(html.includes(`${labels.workstationDashboard.responsibleContext}</h2>`), false);
|
assert.equal(html.includes(`${labels.workstationDashboard.responsibleContext}</h2>`), false);
|
||||||
assert.ok(html.includes(labels.noWorkstationAssets));
|
assert.ok(html.includes(labels.noWorkstationAssets));
|
||||||
@ -850,14 +855,18 @@ test("cabecera es/en: responsable largo o ausente, contexto independiente y seis
|
|||||||
const labels = language === "es" ? assetsSpanish : assetsEnglish;
|
const labels = language === "es" ? assetsSpanish : assetsEnglish;
|
||||||
const header = html.slice(html.indexOf("<header"), html.indexOf("</header>"));
|
const header = html.slice(html.indexOf("<header"), html.indexOf("</header>"));
|
||||||
|
|
||||||
|
assert.equal(header.includes(`href="/companies/${companyId}"`), false);
|
||||||
|
assert.equal(header.includes(company.name), false);
|
||||||
assert.equal(html.includes(`${labels.workstationDashboard.responsibleContext}</h2>`), false);
|
assert.equal(html.includes(`${labels.workstationDashboard.responsibleContext}</h2>`), false);
|
||||||
assert.equal([...html.matchAll(/<h2\b/g)].length, 6);
|
assert.equal([...html.matchAll(/<h2\b/g)].length, 6);
|
||||||
if (personId) {
|
if (personId) {
|
||||||
assert.ok(header.includes(longPerson.name));
|
assert.ok(header.includes(longPerson.name));
|
||||||
|
assert.ok(header.includes('data-slot="avatar"'));
|
||||||
assert.ok(header.includes('aria-haspopup="dialog"'));
|
assert.ok(header.includes('aria-haspopup="dialog"'));
|
||||||
assert.ok(header.includes(`href="/companies/${companyId}/people/${personId}"`));
|
assert.ok(header.includes(`href="/companies/${companyId}/people/${personId}"`));
|
||||||
} else {
|
} else {
|
||||||
assert.ok(header.includes(labels.workstationDashboard.noResponsible));
|
assert.ok(header.includes(labels.workstationDashboard.noResponsible));
|
||||||
|
assert.equal(header.includes('data-slot="avatar"'), false);
|
||||||
assert.equal(header.includes('aria-haspopup="dialog"'), false);
|
assert.equal(header.includes('aria-haspopup="dialog"'), false);
|
||||||
}
|
}
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|||||||
@ -135,11 +135,26 @@ class WorkstationDashboardTests(unittest.TestCase):
|
|||||||
self.capture("S08-compact-summary")
|
self.capture("S08-compact-summary")
|
||||||
|
|
||||||
def test_03_history_edit_software_and_navigation(self):
|
def test_03_history_edit_software_and_navigation(self):
|
||||||
|
for width, height in [(1440, 900), (390, 844), (320, 640)]:
|
||||||
|
self.tool("browser_resize", {"width": width, "height": height})
|
||||||
self.navigate()
|
self.navigate()
|
||||||
self.code("await page.getByRole('button',{name:'Ver historial',exact:true}).click();await page.getByRole('dialog').waitFor();return true;")
|
self.assertEqual(self.code("return await page.getByRole('tab').count();"), 2)
|
||||||
self.assertTrue(self.code("return await page.getByRole('dialog').getByText('Administración — puesto inicial',{exact:true}).isVisible();"))
|
self.assertEqual(self.code("return await page.getByRole('tab',{name:'Información',exact:true}).getAttribute('aria-selected');"), "true")
|
||||||
self.code("await page.keyboard.press('Escape');await page.getByRole('dialog').waitFor({state:'hidden'});return true;")
|
self.assertEqual(self.code("return await page.locator('main header').getByRole('button',{name:'Ver historial',exact:true}).count();"), 0)
|
||||||
self.assertEqual(self.code("return await page.evaluate(()=>document.activeElement?.textContent.trim());"),"Ver historial")
|
if not self.code("return await page.locator('#workstation-configurations').isVisible();"):
|
||||||
|
self.expand_primary()
|
||||||
|
self.capture(f"information-tabs-{width}")
|
||||||
|
self.code("await page.getByRole('tab',{name:'Información',exact:true}).focus();await page.keyboard.press('ArrowRight');await page.keyboard.press('Enter');await page.getByRole('tabpanel',{name:'Historial',exact:true}).waitFor();return true;")
|
||||||
|
self.assertTrue(self.code("return await page.getByRole('tabpanel',{name:'Historial',exact:true}).getByText('Administración — puesto inicial',{exact:true}).isVisible();"))
|
||||||
|
self.assertEqual(self.code("return await page.getByRole('dialog').count();"), 0)
|
||||||
|
self.capture(f"history-tabs-{width}")
|
||||||
|
self.assertLessEqual(self.code("return await page.evaluate(()=>document.documentElement.scrollWidth);"), width)
|
||||||
|
self.code("await page.keyboard.press('ArrowLeft');await page.keyboard.press('Enter');await page.getByRole('tabpanel',{name:'Información',exact:true}).waitFor();return true;")
|
||||||
|
self.assertTrue(self.code("return await page.locator('#workstation-configurations').isVisible();"))
|
||||||
|
self.code("await page.getByRole('button',{name:'Ver historial completo',exact:true}).click();await page.getByRole('tabpanel',{name:'Historial',exact:true}).waitFor();return true;")
|
||||||
|
self.assertEqual(self.code("return await page.evaluate(()=>document.activeElement?.textContent.trim());"), "Historial")
|
||||||
|
self.code("await page.getByRole('tab',{name:'Información',exact:true}).click();return true;")
|
||||||
|
self.tool("browser_resize", {"width": 1440, "height": 900})
|
||||||
self.code("await page.getByRole('button',{name:'Editar puesto',exact:true}).first().click();await page.getByRole('dialog').waitFor();return true;")
|
self.code("await page.getByRole('button',{name:'Editar puesto',exact:true}).first().click();await page.getByRole('dialog').waitFor();return true;")
|
||||||
self.code("await page.keyboard.press('Escape');await page.getByRole('dialog').waitFor({state:'hidden'});return true;")
|
self.code("await page.keyboard.press('Escape');await page.getByRole('dialog').waitFor({state:'hidden'});return true;")
|
||||||
self.code("await page.getByRole('button',{name:'Gestionar software',exact:true}).click();await page.getByRole('dialog').waitFor();return true;")
|
self.code("await page.getByRole('button',{name:'Gestionar software',exact:true}).click();await page.getByRole('dialog').waitFor();return true;")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user