⭐ 0/6
1 / 29
← → navegar · N notas presentador · F pantalla completa
🎙 Notas del presentador
Coding Base 2 · QA Engineering

Coding Classes

Del código suelto a las clases.
El último escalón antes de la arquitectura profesional de automation.

JS modernomap / filterclass + extendsPage Object6 quizzes ⭐

Sesión 2 de la serie. Prerequisito: Coding Base (valores, variables, funciones, loops, objetos, async/await, anatomía de un test). Hoy: el código moderno que ven en specs reales + clases y herencia — EL concepto que toda arquitectura de automation usa. Final: construyen su primer Page Object y lo ven ejecutarse en vivo.

El viaje continúa

Ya hablas el idioma. Hoy aprendes a construir.

✅ Coding Base (sesión 1)

Valores · variables · operadores · funciones · if/else · loops · arrays · objetos · async/await · tu primer test ejecutado.

🎯 Coding Classes (hoy)

JS moderno (el que ves en specs reales) → arrays con superpoderes → clases y herencia → tu primer Page Object.

// El zoom-out sigue creciendo:

función          ← ya la dominasclass            ← hoy: funciones + datos juntos
  ↓
Page Object      ← hoy: una clase que envuelve una página
  ↓
Framework KATA   ← próxima parada: el repo profesional

Recuerda la mecánica del zoom-out de la sesión 1: nada aparece de la nada. La función que dominan es el ladrillo; hoy lo apilan en clases; el Page Object es una clase con propósito de testing; KATA (el framework profesional) es Page Objects con esteroides. Hoy cierran el gap más grande del roadmap.

⭐ QUIZ relámpago · ¿Sigues caliente de la sesión 1?

¿Cómo accedo al email de este objeto?

const creds = { email: "ana@qa.com", password: "secreto123" };
💡 El punto: "del objeto creds, dame email". Lo usaste 20 veces en la sesión 1 — y hoy lo vas a usar con this: this.email significa "de ESTA instancia, dame email". Guarda esa idea.

Calentamiento con doble propósito: reactivar el conocimiento de objetos Y plantar la semilla de this (la explicación menciona this.email). Si la sala falla este quiz, dedica 5 minutos a repasar objetos de Coding Base antes de seguir — clases sin objetos claros no funcionan.

js modernoarrays proclasesherenciapage object

Template literals: texto con huecos

😐 Como lo hacías ayer

const msg = "Hola, " + name + "! Tienes " + bugs + " bugs";

Funciona, pero con 3+ variables se vuelve sopa de comillas y signos +.

😎 Desde hoy

const msg = `Hola, ${name}! Tienes ${bugs} bugs`;

Backticks ` (no comillas) + ${...} = hueco donde entra cualquier variable o expresión.

⌨️

El backtick ` está junto al 1 en teclado US, o AltGr + } en teclado ES/LA. Práctica obligada: lo usarás en cada test.

Concepto trivial pero de uso diario — el spec real está lleno de template literals. Truco de retención: "string con huecos". Importante señalar dónde está el backtick en SU teclado — es la fricción real #1 con esta sintaxis. Todo lo de sesión 1 con "+" sigue siendo válido; esto es ergonomía.

js modernoarrays proclasesherenciapage object

Arrow functions: ya las usabas sin saberlo 🏹

La misma función, en 3 pasos de transformación:

// 1. La clásica de la sesión 1
function double(n) { return n * 2; }

// 2. Misma función, con flecha
const double = (n) => { return n * 2; };

// 3. Una sola expresión → return implícito
const double = (n) => n * 2;

🏹 La flecha =>

Se lee: "recibe n → devuelve n * 2". Función compacta, ideal para pasarla a OTRA función.

👀 ¿Te suena de algún lado?

test("login", async ({ page }) => {
  ...
});

¡Tu test de la sesión 1 ERA una arrow function! Hoy por fin sabes leer cada símbolo.

El momento "¡ahhh!" del acto: el test() que escribieron en sesión 1 ya usaba arrow + async. La transformación en 3 pasos es el camino mental — muéstrala despacio. Para qué sirve la compacidad: pasarle funciones a map/filter (siguiente acto). No menciones diferencias de this entre function y arrow — ruido innecesario en este nivel.

js modernoarrays proclasesherenciapage object

Destructuring: desempacar cajas 📤

const user = { name: "Ana", role: "admin" };

// ayer: uno por uno
const name = user.name;
const role = user.role;

// hoy: desempaca de un tirón
const { name, role } = user;

📤 La regla

Llaves a la IZQUIERDA del = significan "extrae estas propiedades del objeto de la derecha". Los nombres deben coincidir.

🎭 El misterio resuelto

async ({ page }) => { ... }

Playwright entrega un objeto LLENO de herramientas. ({ page }) = "del paquete que me das, solo desempaco page". Misterio de la sesión 1: cerrado. 🔓

Segundo "¡ahhh!" del acto: ({ page }) por fin explicado. Playwright pasa un objeto de fixtures (page, request, context, browser...) y el test desempaca lo que necesita. Cuando lleguen a KATA verán ({ api }), ({ ui }), ({ test }) — exactamente el mismo patrón. Esa conexión vale la sesión entera.

🎮 Playground en vivo

JS moderno, a tus dedos

modern.js — editable
▶ Presiona Ejecutar para ver la salida…

Los 3 conceptos del acto en una celda. Error esperable del reto 1: usar comillas normales en vez de backticks → ${} sale literal en el texto. Es el error PERFECTO: muéstralo y todos recordarán la diferencia para siempre.

⭐ QUIZ 1 · JS moderno

En test("login", async ({ page }) => {...}) — ¿qué hace ({ page })?

💡 Destructuring en los parámetros: Playwright llama a tu función pasándole un objeto lleno de fixtures (page, request, context…) y tú desempacas solo lo que usas. Por eso a veces verás ({ page, request }) — desempacando dos.

Valida el concepto más transferible del acto. La mención de ({ page, request }) prepara terreno para tests híbridos UI+API que verán en Playwright Pro. Si dudan entre A y B: vuelve al ejemplo de user = {name, role} — mismo patrón, solo que dentro de paréntesis de función.

js modernoarrays proclasesherenciapage object

.map(): transformar cada elemento 🔄

const users = ["ana", "luis", "mia"];

const emails = users.map(u => `${u}@qa.com`);

// ["ana@qa.com", "luis@qa.com", "mia@qa.com"]

Se lee: "por cada u del array, devuélveme `${u}@qa.com`". Array nuevo, mismo tamaño, elementos transformados.

🆚 El loop de ayer

const emails = [];
for (const u of users) {
  emails.push(`${u}@qa.com`);
}

Mismo resultado, 4 líneas. map lo dice en 1: arrow functions pagando dividendos.

🧪 En testing

Generar emails de prueba, extraer ids de una respuesta de API, preparar la columna de datos que el test necesita.

map/filter/find existen porque las arrow functions hacen barato pasar "mini-recetas" a otros métodos. Lectura en voz alta del patrón: "por cada u, devuélveme X". El loop de ayer NO queda obsoleto — map es el atajo para el caso más común (transformar lista completa).

js modernoarrays proclasesherenciapage object

.filter() y .find(): elegir con criterio 🎯

🚿 filter — todos los que cumplen

const users = [
  { name: "Ana",  role: "admin"  },
  { name: "Luis", role: "viewer" },
  { name: "Mia",  role: "admin"  },
];

const admins = users.filter(u => u.role === "admin");
// [Ana, Mia] — array, puede venir vacío []

🔍 find — el primero que cumple

const luis = users.find(u => u.name === "Luis");
// { name: "Luis", role: "viewer" }
// UN objeto — o undefined si nadie cumple
🧪

Tu tabla de test data, filtrada con código: "dame solo los admins", "encuentra el usuario inactivo". La condición es la MISMA del if/else de ayer.

Distinción clave: filter devuelve ARRAY (posiblemente vacío), find devuelve UN elemento (o undefined — conecta con el undefined que vieron en sesión 1). La condición dentro es una expresión booleana idéntica a las del if. Trío completo: map transforma, filter selecciona varios, find busca uno.

🎮 Playground en vivo

Tu test data, dominada

data-power.js — editable
▶ Presiona Ejecutar para ver la salida…

El reto 2 introduce encadenamiento (.filter().map()) — patrón que verán constantemente en código real. Si alguien pregunta por qué console.log(names) imprime con corchetes: los arrays se imprimen completos, útil para depurar. Deja 3-4 minutos de juego libre con los datos.

⭐ QUIZ 2 · Arrays con superpoderes

¿Qué devuelve [1, 2, 3, 4].filter(n => n > 2)?

💡 filter = colador: deja pasar los elementos cuya condición da true y devuelve un array nuevo con ellos. La opción B describe a find (que devolvería 3, sin corchetes) — esa es exactamente la diferencia entre ambos.

La opción B está diseñada para discutir filter vs find una vez más — distinción que en código real causa bugs (esperar un objeto y recibir un array). Cierre del acto: ya saben transformar y seleccionar datos como profesionales.

js modernoarrays proclasesherenciapage object

El problema: datos y funciones viven separados

😵 Todo suelto

const tester1 = { name: "Ana", bugs: 0 };
const tester2 = { name: "Luis", bugs: 0 };

function findBug(tester) {
  tester.bugs = tester.bugs + 1;
}
// ¿qué funciones van con qué objetos?
// ¿quién me garantiza que tester tiene .bugs?

🧩 El síntoma

Objetos por un lado, funciones por otro. Con 5 objetos y 15 funciones, nadie sabe qué combina con qué.

💡 La idea

Una clase junta en un solo lugar: los datos (propiedades) + las funciones que operan sobre ellos (métodos). Un molde 🍪 que fabrica objetos ya equipados.

Motivación ANTES de sintaxis: el dolor es real (desorden de funciones y objetos sueltos) y la clase es la solución natural. Analogía molde de galletas: la clase es el MOLDE, los objetos que fabrica son las GALLETAS — cada una independiente pero con la misma forma. Esa analogía carga toda la sección.

js modernoarrays proclasesherenciapage object

Anatomía de una clase 🏗️

class Tester {
  constructor(name) {
    this.name = name;
    this.bugs = 0;
  }

  findBug() {
    this.bugs = this.bugs + 1;
    return `${this.name}: ${this.bugs} bugs`;
  }
}

class Tester

"Defino el molde Tester." Nombre SIEMPRE con mayúscula inicial — convención universal.

constructor(name)

La función que corre automáticamente al fabricar cada objeto. Recibe los datos iniciales.

this

"Este objeto en particular" — la galleta que se está fabricando o usando, no el molde.

findBug()

Un método: función que vive DENTRO de la clase y trabaja con los datos de this. Sin la palabra function.

Cuatro piezas, una por tarjeta. this es LA dificultad — definición operativa: "el objeto concreto con el que se está trabajando ahora". No filosofar más: la siguiente slide fabrica instancias y la 16 lo muestra paso a paso en memoria. Detalle sintáctico: métodos sin la palabra function, sin comas entre ellos.

js modernoarrays proclasesherenciapage object

new: fabricar galletas 🍪

const ana  = new Tester("Ana");
const luis = new Tester("Luis");

ana.findBug();  // "Ana: 1 bugs"
ana.findBug();  // "Ana: 2 bugs"
luis.findBug(); // "Luis: 1 bugs" ← ¡SU propio contador!

🏭 new Tester("Ana")

"Fabrica un objeto con el molde Tester" → corre el constructor con name = "Ana" → entrega la instancia.

🍪 Cada instancia, su memoria

ana.bugs y luis.bugs son contadores independientes. El molde es uno; las galletas, muchas.

👀 Spoiler

page de Playwright… es una instancia de una clase. page.click() = método. Llevas una sesión entera usando clases.

Independencia de instancias = concepto del quiz 3, márcalo fuerte: cada new crea memoria nueva. El spoiler de page es otro puente potente: no van hacia territorio desconocido, van hacia entender lo que YA usan. Pregunta de control antes de seguir: "si hago new Tester('Mia') y llamo findBug una vez, ¿cuánto vale mia.bugs?"

🧠 Visualizador de memoria

this, paso a paso

class User {
constructor(email) {
this.email = email;
}
greet() {
return `Hola, ${this.email}`;
}
}
const ana = new User("ana@qa.com");
console.log(ana.greet());
paso 0 / 6

📦 Memoria

— vacía —

Presiona "Siguiente paso": vas a ver a quién apunta this en cada momento.
consola…

La slide más importante de la sesión. Pausa en el paso 3 (this → instancia ana durante el constructor) y en el 5 (this → ana durante greet). Mensaje final: this apunta SIEMPRE al objeto a la izquierda del punto en la llamada — ana.greet() → this es ana. Con esa regla resuelven el 95% de los casos que verán.

🎮 Playground en vivo

Fabrica tus propias instancias

tester-class.js — editable
▶ Presiona Ejecutar para ver la salida…

Primera clase escrita por ellos. El reto (método report) valida la sintaxis de métodos: sin function, usando this. Error esperable: olvidar this y escribir name a secas → ReferenceError: name is not defined — error pedagógicamente perfecto: dentro de un método, los datos de la instancia SIEMPRE viajan con this.

⭐ QUIZ 3 · Clases e instancias

Después de este código, ¿cuánto vale luis.bugs?

const ana  = new Tester("Ana");
const luis = new Tester("Luis");
ana.findBug();
ana.findBug();
ana.findBug();
💡 new fabrica objetos independientes: los 3 findBug() fueron sobre ana (this = ana), así que luis.bugs sigue en su valor inicial 0 del constructor. El molde es uno, cada galleta lleva su propia cuenta. 🍪

Valida los DOS conceptos críticos juntos: independencia de instancias + this resolviéndose al objeto de la llamada. La opción C tienta a quien cree que las propiedades solo existen si se usan — el constructor ya las inicializó. Si aciertan masivamente: el acto más difícil está ganado.

js modernoarrays proclasesherenciapage object

Nuevo dolor: clases que se repiten 😩

class Admin {
  constructor(name) { this.name = name; }
  login() { return `${this.name} entró`; }
  deleteProject() { return "💥 borrado"; }
}
class Viewer {
  constructor(name) { this.name = name; }
  login() { return `${this.name} entró`; }
  watch() { return "👀 mirando"; }
}
🚨

constructor y login() están copiados y pegados. ¿Te suena? Es el mismo dolor de los test cases repetidos de la sesión 1 — y la solución tiene la misma forma: escribir lo común UNA vez.

Herencia motivada por el mismo principio que motivó funciones en sesión 1: no repetir. Admin y Viewer comparten constructor y login; difieren en un método propio cada uno. Pregunta a la sala: "¿dónde han visto este copy-paste antes?" — la respuesta (test cases duplicados) les pertenece y hace suya la solución.

js modernoarrays proclasesherenciapage object

extends: heredar lo común 🧬

class User {
  constructor(name) { this.name = name; }
  login() { return `${this.name} entró`; }
}

class Admin extends User {
  deleteProject() { return "💥 borrado"; }
}

const ana = new Admin("Ana");
ana.login();         // heredado del padre ✅
ana.deleteProject(); // propio de Admin ✅

🧬 extends

"Admin es un User, más sus extras." Hereda constructor, propiedades y métodos del padre — gratis.

📞 super(...)

Si el hijo define su PROPIO constructor, la primera línea debe ser super(name): "padre, haz tu parte primero". Si no define constructor, hereda el del padre directo.

🥋 Por qué te importa

El framework KATA al que vas es una cadena de extends: tu Page hereda docenas de helpers ya escritos. Entender esto = leer KATA sin miedo.

Lectura de extends: "es un... más extras". super solo cuando el hijo define constructor propio — el ejemplo lo evita a propósito para mostrar el caso simple primero; el playground siguiente SÍ usa super. La tarjeta KATA es el porqué de toda la sesión: en el repo profesional, LoginPage extends UiBase extends TestContext — leerán esa cadena con naturalidad.

🎮 Playground en vivo

Herencia en acción

inheritance.js — editable
▶ Presiona Ejecutar para ver la salida…

Aquí super aparece con su comentario inline. Experimento valioso en vivo: borrar la línea super(name) y ejecutar → "ReferenceError: Must call super constructor..." — el lenguaje OBLIGA a llamar al padre primero. El reto Viewer es deliberadamente simétrico al ejemplo de la slide 19: cierra el círculo del dolor → solución.

⭐ QUIZ 4 · Herencia

En class Admin extends User — ¿qué hace super(name)?

💡 super(name) = "padre, ejecuta TU constructor con este dato". Así User asigna this.name y luego Admin agrega lo suyo. Sin esa llamada, el lenguaje lanza error: el hijo no puede usar this antes de que el padre haga su parte.

Último concepto duro de la sesión. Si vieron el experimento de borrar super en el playground, este quiz es cosecha directa. Con esto cerrado, todo lo que queda (Page Object) es APLICAR lo aprendido — anúncialo así para bajar la tensión: "lo difícil ya pasó".

js modernoarrays proclasesherenciapage object

Mira este spec real. ¿Notas algo? 🔍

login.spec.ts — 3 tests
test("login exitoso", async ({ page }) => {
  await page.fill("#email", "ana@qa.com");     
  await page.fill("#password", "secreto123");  │ los mismos
  await page.click("#login-btn");              ┘ 3 pasos…
  await expect(page.locator("#welcome")).toBeVisible();
});

test("login con password inválida", async ({ page }) => {
  await page.fill("#email", "ana@qa.com");     
  await page.fill("#password", "malamala");    │ …otra vez…
  await page.click("#login-btn");              
  await expect(page.locator("#error-msg")).toBeVisible();
});

// test 3, test 4, test 5… ¿y si mañana #login-btn cambia de id? 💀

Deja que ELLOS nombren el problema antes de decirlo: pasos duplicados + selectores regados por todos lados. La pregunta asesina: "si el dev renombra #login-btn, ¿cuántos lugares corriges?" Con 50 tests, 50 lugares. Ya saben la herramienta para encapsular: una clase. La siguiente slide la presenta con nombre y apellido.

js modernoarrays proclasesherenciapage object

Page Object: una clase que envuelve una página 🎁

class LoginPage {
  constructor(page) {
    this.page = page;   // guarda el "control remoto"
  }

  async login(email, password) {
    await this.page.fill("#email", email);
    await this.page.fill("#password", password);
    await this.page.click("#login-btn");
  }
}

🎁 La jugada

TODO lo que se puede hacer en la página de login, encapsulado en UNA clase. Los selectores viven aquí y solo aquí.

📺 constructor(page)

Recibe el page de Playwright y lo guarda en this.page — el control remoto del navegador, ahora de la instancia.

🧬 Todo lo de hoy, junto

class ✓ · constructor ✓ · this ✓ · método async ✓ · await ✓. Cero conceptos nuevos — solo composición.

Énfasis: NO hay nada nuevo en esta slide — es la clase de Tester con page adentro. this.page.fill = "del control remoto de ESTA instancia, llama fill". Nombre oficial: Page Object Model (POM), patrón estándar de la industria — suelta el término para que lo reconozcan en artículos y entrevistas.

El refactor: antes / después

❌ Antes — 8 líneas por test
test("login exitoso", async ({ page }) => {
  await page.fill("#email", "ana@qa.com");
  await page.fill("#password", "secreto123");
  await page.click("#login-btn");
  await expect(page.locator("#welcome"))
    .toBeVisible();
});
✅ Después — el test cuenta una historia
test("login exitoso", async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.login("ana@qa.com", "secreto123");
  await expect(page.locator("#welcome"))
    .toBeVisible();
});
🏆

El test ahora se lee como un test case manual: "crea la página de login, entra con estas credenciales, verifica la bienvenida". Y si #login-btn cambia → corriges 1 línea dentro de LoginPage, no 50 tests.

Doble victoria a destacar: legibilidad (el test se lee como su test case manual — círculo completo con la slide 2 de Coding Base) y mantenimiento (selector cambia → 1 solo lugar). La aserción se queda en el test a propósito: las acciones van al Page Object, los expect se quedan donde se decide pasa/falla. Esa división reaparece en KATA.

🎮 Playground · El gran momento

Tu Page Object, ejecutándose en vivo 🤖

https://bunkai-bank.test/login

🏦 Bunkai Bank

Acceso a tu cuenta

✅ ¡Bienvenida, Ana!
❌ Credenciales inválidas
login-page.spec.ts — ¡código real, clases reales!
▶ Ejecuta — esta vez tu CLASE controla el cursor…

Clímax de la serie: a diferencia del simulador de Coding Base (que interpretaba línea a línea), este EJECUTA el JavaScript de verdad — su clase LoginPage corre tal cual correría en Playwright. Demos en vivo: (1) ejecutar tal cual → PASS; (2) password incorrecta → FAIL con expected/received; (3) el experimento estrella: cambiar "#login-btn" por "#boton-entrar" DENTRO de la clase → error "No existe el elemento" → corregir EN UN SOLO LUGAR → verde. Ese momento ES el argumento del Page Object.

⭐ QUIZ 5 · El integrador

El dev renombra #login-btn a #signin-btn. Tienes 50 tests que hacen login usando LoginPage. ¿Cuántos lugares corriges?

💡 Esa es TODA la promesa del Page Object: los selectores viven en un único lugar. Los 50 tests llaman loginPage.login(...) y ni se enteran del cambio. Acabas de entender por qué TODA arquitectura profesional de automation (incluido KATA) se construye sobre clases.

Si hicieron el experimento del selector en el simulador, este quiz es la formalización de lo que VIVIERON. La opción B caza al optimista mágico — ninguna herramienta adivina renombres. Respuesta correcta masiva aquí = sesión cumplida.

Próxima parada

KATA es esto mismo… con esteroides 🥋

TestContext config, datos fake, utilidades
↓ extends
UiBase helpers de Playwright ya escritos
↓ extends
⭐ LoginPage ¡ESTO ya lo sabes construir!
↓ usada por
TestFixture el ({ page }) pero con tus clases
↓ usada por
login.spec.ts tests que cuentan historias
🗺️

Cada capa es una clase que extends la anterior — la cadena de herencia que acabas de dominar. Tu LoginPage de hoy es el corazón del framework profesional.

Teaser, NO lección: no expliques cada capa — solo muestra que la estrella verde (la Page que construyeron HOY) es el centro del framework, y que el resto son las mismas dos herramientas de esta sesión (extends + fixtures/destructuring) aplicadas con más capas. El miedo a la arquitectura muere cuando reconocen las piezas. Próximos pasos del roadmap: práctica real con Playwright contra el dojo, luego el puente KATA completo.

Misión cumplida

Tu puntaje final

— / 6

🏠

Re-practica en casa

Este archivo funciona offline: re-juega los 4 playgrounds y los retos.

🧪

Reto puente

Escribe un SignupPage imaginario en papel: ¿qué métodos tendría? ¿qué selectores guardaría?

🥷

Próxima sesión

Playwright real contra UPEX Dojo: instalas el kit y tus Page Objects controlan un navegador DE VERDAD. 🚀

Hoy: JS moderno → map/filter/find → clases → this → herencia → tu primer Page Object. El gap más grande del camino: cerrado.

Celebra el score y nombra el logro exacto: hoy cruzaron la frontera entre "escribir scripts" y "construir arquitectura". El reto puente (SignupPage en papel) consolida sin computadora. Anuncia la sesión Dojo Lab: misma LoginPage, navegador real, app real (dojo.upexgalaxy.com). Comparte el archivo HTML.