Saltar al contenido

El smoke test del checkout antes de Black Friday y la fecha en que dejas de cambiar cosas

En la práctica/ 17 min de lectura

Una prueba de Playwright que recorre producto, carrito y checkout sin crear pedidos, en temas de vista previa y cada hora, más una fecha de congelamiento con proceso para urgencias.

  • Shopify
  • Playwright
  • QA
  • GitHub Actions
En este artículo (7)
  1. Qué es un smoke test y por qué una prueba vale más que cincuenta
  2. La prueba, línea por línea
  3. Ir más lejos, solo en una tienda de desarrollo
  4. Antes de publicar un tema y después de instalar una app
  5. Correrla cada hora con GitHub Actions
  6. La fecha de congelamiento
  7. La lista para la semana antes de Black Friday

Cada noviembre pasa lo mismo en alguna tienda. Se actualiza una app, una sección recibe un ajuste nuevo, alguien pega un snippet de seguimiento en el tema, y el jueves por la noche el botón de agregar al carrito abre un cajón que nunca se llena. Nadie se entera hasta que la conversión cae en la mañana más concurrida del año, porque nadie recorrió el camino al checkout después del cambio.

Esta guía trata de dos cosas baratas que evitan casi todo eso: una prueba automática que recorre el camino que hace un comprador hasta el checkout, y una fecha a partir de la cual la tienda deja de cambiar. Ninguna es ingeniosa. Las dos tienen que existir antes que el tráfico. Black Friday 2026 es el viernes 27 de noviembre, así que, al momento de escribir esto, quedan cinco semanas, que alcanzan.

Si buscas la revisión manual completa para un lanzamiento, con cantidades, combinaciones de descuentos, redirecciones y estados vacíos, es otro trabajo y está en un QA previo al lanzamiento que sí encuentra errores. Este artículo es la parte que se corre una y otra vez, sin una persona.

Qué es un smoke test y por qué una prueba vale más que cincuenta

Un smoke test responde una sola pregunta: ¿está abierto el camino principal? El nombre viene del hardware: se enciende la placa y se mira si echa humo antes de probar nada más fino. En una tienda, el camino principal es ficha de producto, agregar al carrito, carrito y checkout. Si cualquiera de esos cuatro pasos se rompe, ese día no importa nada más de lo que pruebes.

La tentación es escribir una suite: cada plantilla, cada filtro, cada popup, cada idioma. Las suites grandes tienen una vida predecible. Se escriben de golpe, se rompen con el primer cambio de texto, alguien marca como omitidas las que hacen ruido, y para el segundo mes nadie las corre porque un resultado en rojo ya no significa nada. Una sola prueba que solo falla cuando un comprador no pudo llegar al checkout conserva su significado. Cuando está en rojo, alguien se levanta.

Por eso las reglas de esta prueba son estrictas. Toca un solo producto que siempre tiene stock. Usa selectores que describen lo que ve el comprador, no cómo está construido el tema, para que una refactorización de CSS no la rompa. Y se detiene en el checkout. En una tienda en vivo nunca escribe un número de tarjeta, porque las únicas formas de terminar ahí un pedido de prueba generan un cobro real o bloquean a los clientes reales, como se explica más abajo.

Desliza la figura para verla completa

La prueba, línea por línea

Usa Playwright Test. Asume un proyecto con @playwright/test instalado y una carpeta tests/smoke. Todo valor que cambia entre tiendas sale de una variable de entorno, así que el mismo archivo corre contra la tienda en vivo, un tema en vista previa o una tienda de desarrollo.

// tests/smoke/checkout-path.spec.ts
import { test, expect } from '@playwright/test';

const PRODUCT_PATH = process.env.SMOKE_PRODUCT_PATH || '/products/your-best-seller';
const PREVIEW_THEME_ID = process.env.PREVIEW_THEME_ID || '';
const STOREFRONT_PASSWORD = process.env.STOREFRONT_PASSWORD || '';

// La misma URL de la tienda, renderizada por un tema sin publicar si hay un ID.
function onTheme(path: string): string {
  if (!PREVIEW_THEME_ID) return path;
  return path + (path.includes('?') ? '&' : '?') + 'preview_theme_id=' + PREVIEW_THEME_ID;
}

test('a shopper can get from a product page to checkout', async ({ page }) => {
  if (STOREFRONT_PASSWORD) {
    // El mismo formulario que envía la página de contraseña. La cookie queda en este contexto.
    await page.request.post('/password', {
      form: { form_type: 'storefront_password', utf8: '✓', password: STOREFRONT_PASSWORD },
    });
  }

  await page.goto(onTheme(PRODUCT_PATH));
  await expect(page).not.toHaveURL(/\/password/);

  const heading = page.getByRole('heading', { level: 1 }).first();
  await expect(heading).toBeVisible();
  const title = (await heading.innerText()).trim();

  const added = page.waitForResponse(
    (res) => res.url().includes('/cart/add') && res.request().method() === 'POST',
  );
  await page.getByRole('button', { name: /add to cart/i }).click();
  expect((await added).ok()).toBe(true);

  await page.goto(onTheme('/cart'));
  await expect(page.getByRole('link', { name: title }).first()).toBeVisible();

  await page.getByRole('button', { name: /check ?out/i }).click();
  await expect(page).not.toHaveURL(/\/cart\/?(\?|$)/);
  await expect(page.getByText(title).first()).toBeVisible();
  await expect(page.getByText('Total', { exact: true }).first()).toBeVisible();
  // Aquí termina. No se escribe nada después de este punto, así que no se crea ningún pedido.
});

Qué hace cada parte:

  • Las tres constantes. SMOKE_PRODUCT_PATH es el producto que la prueba mete al carrito. Elige uno que siempre tenga stock, con una sola variante o una preseleccionada, y que no forme parte de un cambio de precio de Black Friday, para que la prueba no falle porque el equipo de merchandising hizo su trabajo. PREVIEW_THEME_ID y STOREFRONT_PASSWORD quedan vacías salvo que las necesites.
  • onTheme(). Agrega preview_theme_id a las URLs de la tienda. Ese parámetro es la forma en que Shopify renderiza una página con un tema no publicado: la salida JSON de shopify theme push devuelve un preview_url con la forma https://your-store.myshopify.com/?preview_theme_id=<id>. La prueba lo agrega a la ficha de producto y al carrito, las dos páginas que renderiza el tema en este camino. El checkout lo renderiza Shopify, no el tema.
  • El paso de la contraseña. Solo corre si hay una contraseña definida. El formulario storefront_password de Shopify envía un POST a /password con un campo oculto form_type. page.request comparte cookies con el contexto del navegador, según la guía de pruebas de API, así que la cookie de sesión que pone la tienda ya está ahí cuando se abre la página. Enviar el formulario directamente evita depender de cómo cada tema arma su página de contraseña, que cambia mucho de un tema a otro.
  • not.toHaveURL(/\/password/). Si la contraseña es incorrecta, la tienda te regresa a la página de contraseña y la prueba falla aquí con un mensaje claro, en vez de fallar tres pasos después por un botón que no aparece.
  • El encabezado. getByRole('heading', { level: 1 }) lee el título del producto como lo haría un lector de pantalla. La guía de locators de Playwright recomienda priorizar los locators por rol, porque siguen lo que percibe el usuario. El título se guarda para que los pasos siguientes busquen ese producto exacto.
  • waitForResponse antes del clic. La promesa se crea antes de hacer clic para que la respuesta no se escape. Acepta cualquier POST a /cart/add, lo que cubre los temas que llaman a /cart/add.js y los que envían a /cart/add. Revisar ok() atrapa el 422 que devuelve la Ajax Cart API para una variante agotada.
  • El carrito. Ir directo a /cart es a propósito. Que un tema abra un cajón, una notificación o una página nueva es una decisión de diseño; la página del carrito es el único lugar que todos los temas tienen. La aserción es que se vea un enlace con el nombre del producto.
  • El botón de checkout. /check ?out/i acepta "Checkout" y "Check out". Si tu tienda está en español, cambia el patrón por el texto de tu botón, por ejemplo /pagar|finalizar/i, y haz lo mismo con /add to cart/i, por ejemplo /agregar al carrito/i.
  • Las tres aserciones finales. La URL ya salió del carrito, el título del producto aparece en el resumen del pedido y la palabra Total está en la página. Las aserciones web-first de Playwright reintentan hasta pasar o agotar el tiempo, así que no hay esperas fijas. La prueba no verifica la forma de la URL del checkout, porque Shopify no la garantiza.

El archivo de configuración toma la URL base de una variable de entorno, así que page.goto('/cart') se resuelve contra la tienda a la que apuntes, como explica la configuración de pruebas de Playwright:

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

if (!process.env.STORE_URL) throw new Error('Set STORE_URL, e.g. https://your-store.myshopify.com');

export default defineConfig({
  testDir: 'tests/smoke',
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 1 : 0,
  reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'list',
  expect: { timeout: 15_000 },
  use: {
    baseURL: process.env.STORE_URL,
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
  },
  projects: [{ name: 'mobile-chrome', use: { ...devices['Pixel 7'] } }],
});

Tres decisiones que vale la pena explicar. El proyecto emula un teléfono, porque un camino al checkout que solo funciona en escritorio es el fallo que nadie ve desde la oficina; revisa el reparto por dispositivo en tu propia analítica y agrega un proyecto de escritorio si el escritorio también trae ingresos reales. El tiempo de espera de las aserciones es mayor que los cinco segundos por defecto porque el checkout carga más que una página del tema. Y un reintento en CI absorbe un tropiezo de red sin ocultar una rotura real: una prueba que falla dos veces seguidas es un fallo real.

Córrela en local con STORE_URL=https://your-store.myshopify.com npx playwright test. En PowerShell de Windows, define antes la variable con $env:STORE_URL="…". Dos cosas suelen hacerla fallar la primera vez sin que nada esté roto: un popup de newsletter o de cookies que tapa el botón, y un producto con una variante que hay que elegir. Para el popup, Playwright tiene page.addLocatorHandler(), que cierra un overlay cada vez que aparece. Para la variante, elige otro producto.

Ir más lejos, solo en una tienda de desarrollo

La prueba se detiene en el checkout porque terminar un pedido en una tienda en vivo cuesta algo de cualquier forma. La documentación de pedidos de prueba de Shopify da las opciones: activar la pasarela de pago de prueba o el modo de prueba de tu proveedor, o pagar con una tarjeta real y luego cancelar y reembolsar. La primera es explícita sobre su efecto: los clientes no pueden hacer pedidos reales mientras tus proveedores de pago están en modo de prueba. La segunda puede dejar comisiones del procesador. Ninguna de las dos va en una ejecución automática en noviembre.

Una tienda de desarrollo es donde la prueba puede llegar hasta el final. Las tiendas de desarrollo no procesan transacciones reales, aceptan pedidos de prueba ilimitados con la pasarela de prueba y siempre están protegidas con contraseña, que es la razón de que exista el paso de la contraseña. Ahí puedes extender la prueba más allá de la última aserción: llenar los campos de contacto y dirección con getByLabel(), usar un correo que termine en @example.com como recomienda Shopify, y pagar con los números de tarjeta de la pasarela de prueba: 1 para una transacción aprobada, 2 para una rechazada, 3 para un fallo de la pasarela. El caso rechazado merece su propia prueba, porque casi nadie revisa el mensaje que lee un comprador después de un pago fallido. Si la tienda de desarrollo tiene una app de fulfillment, desactívala antes, como aconseja la guía para procesar pedidos de prueba.

Mantén las dos versiones en archivos separados. La de la tienda en vivo nunca debe tener una línea capaz de pagar.

Antes de publicar un tema y después de instalar una app

Los dos momentos de noviembre en que el camino se rompe son la publicación de un tema y un cambio en una app, y la prueba cubre los dos.

Antes de publicar un tema. Sube la versión nueva como tema sin publicar con shopify theme push --unpublished --json, toma el ID del preview_url y corre la prueba contra él con PREVIEW_THEME_ID definida. La tienda en vivo sigue vendiendo con el tema anterior mientras el nuevo se recorre de la ficha al checkout. Si prefieres el admin, la acción de vista previa de la biblioteca de temas hace lo mismo; ten en cuenta que un enlace de vista previa compartible caduca dos días después de crearse, así que no lo repartas el primer día del congelamiento esperando que funcione el cuarto. La ayuda de Shopify también advierte que un preview_theme_id que queda en URLs en vivo fuerza la barra de vista previa para todos los visitantes, otra razón para mantenerlo dentro de la prueba y fuera de los enlaces que publicas.

Después de instalar o actualizar una app. Las apps que agregan app embeds, scripts o extensiones de checkout cambian el camino en vivo en el momento en que guardas, sin paso de vista previa. Corre la prueba contra la tienda en vivo justo después del cambio, y otra vez cuando termine el asistente de configuración de la app, porque algunas apps solo inyectan su código al final del onboarding. Si sale en rojo, desactiva primero el app embed o la extensión, e investiga cuando el camino vuelva a estar abierto.

Correrla cada hora con GitHub Actions

El workflow de abajo sigue la guía de CI de Playwright, con un cambio: en vez de correr en cada push, corre por horario y a demanda. Una tienda se rompe porque cambió la tienda, no porque cambió el repositorio, así que el disparador que importa es el reloj.

# .github/workflows/checkout-smoke.yml
name: Checkout smoke test
on:
  schedule:
    - cron: '17 * * * *'   # cada hora en el minuto 17, UTC
  workflow_dispatch:
    inputs:
      preview_theme_id:
        description: 'Unpublished theme ID to test (empty = live theme)'
        required: false
        default: ''
jobs:
  smoke:
    timeout-minutes: 15
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: lts/*
      - name: Install dependencies
        run: npm ci
      - name: Install Chromium
        run: npx playwright install --with-deps chromium
      - name: Run smoke test
        run: npx playwright test
        env:
          STORE_URL: ${{ secrets.STORE_URL }}
          STOREFRONT_PASSWORD: ${{ secrets.STOREFRONT_PASSWORD }}
          SMOKE_PRODUCT_PATH: ${{ vars.SMOKE_PRODUCT_PATH }}
          PREVIEW_THEME_ID: ${{ inputs.preview_theme_id }}
      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

La URL de la tienda y la contraseña de la tienda van en secretos del repositorio, como describe GitHub en usar secretos en GitHub Actions, y llegan a la prueba como variables de entorno, nunca como texto en el archivo. La contraseña de la tienda es la de visitantes, en Tienda online, Preferencias, no un acceso al admin; la documentación de tiendas de desarrollo de Shopify dice explícitamente que no reutilices ahí la contraseña del admin. La ruta del producto no es secreta, así que va como variable normal del repositorio. Solo se instala Chromium porque la configuración solo usa un dispositivo Chromium, lo que mantiene corta cada ejecución.

Detalles de la referencia de disparadores de GitHub que importan aquí: las ejecuciones programadas usan UTC por defecto, corren sobre la rama por defecto y pueden retrasarse cuando GitHub Actions está bajo mucha carga, algo plausible el último viernes de noviembre. Por eso el minuto 17 y no el minuto 0, y por eso el proceso de arreglos urgentes de abajo corre la prueba a mano con workflow_dispatch en vez de esperar al horario. En un repositorio público, los horarios se desactivan tras 60 días sin actividad, así que revisa que el workflow siga activo la primera semana de noviembre.

Dos notas prácticas. Cada ejecución es una visita real con un agregar al carrito real, así que aparece en la analítica de la tienda; una vez por hora es un error de redondeo, una vez cada cinco minutos no. Y si la tienda está detrás de una capa de protección contra bots, los runners de GitHub pueden recibir un desafío; permítelos o corre el mismo comando desde una máquina que controles. Asegúrate de que una ejecución fallida avise a una persona, no solo a un canal que nadie lee durante una venta.

La fecha de congelamiento

Un smoke test te dice que el camino está abierto ahora. Un congelamiento hace probable que siga abierto. La idea es sencilla: desde una fecha fija hasta que termina la venta, no cambia nada en la tienda ni en el checkout salvo que cumpla una definición escrita de urgente.

Nuestra recomendación, que debes adaptar a tu calendario: el último cambio grande sale en BF−14, el viernes 13 de noviembre, para que tenga una semana completa de tráfico real antes del congelamiento. El congelamiento empieza en BF−7, el viernes 20 de noviembre al final de la jornada, y termina el martes 1 de diciembre, el día después del Cyber Monday. Escribe la fecha, la hora y la zona horaria en el calendario del equipo y en el acuerdo con cada agencia y freelance que tenga acceso a la tienda.

Qué se congela: el código y los ajustes del tema en vivo; instalar, quitar o reconfigurar apps que tocan la tienda o el checkout; despliegues de extensiones de checkout y Functions; scripts de seguimiento y píxeles personalizados; cambios de navegación y de URLs. Qué no se congela: contenido de productos, imágenes y stock; colecciones y orden de merchandising; los propios descuentos de Black Friday, siempre que se hayan creado y probado antes del congelamiento y solo se activen por programación.

Qué cuenta como urgente: algo que impide comprar o hace pagar un importe equivocado. Un agregar al carrito roto, un checkout que no carga, un precio incorrecto, un descuento que no se aplica o se aplica dos veces, impuestos o tarifas de envío incorrectos, un problema de seguridad o legal. Un seguimiento de compras roto solo califica si hay campañas pagadas gastando contra él esa semana y el arreglo es de configuración, no de código. Un banner desalineado, una errata fuera de precios y textos legales, una petición de funcionalidad nueva y "una app pequeña que queremos probar" no son urgentes. Van a una lista para el 2 de diciembre.

Desliza la figura para verla completa

El proceso para cada arreglo urgente:

  1. Repórtalo con la entrada que lo causa. Producto, variante, cantidad, código de descuento, dispositivo. "El carrito se ve raro" no es un reporte.
  2. Aprobación. Dos personas, nombradas antes de que empiece el congelamiento: una responsable del negocio que pueda decir "sí, esto nos cuesta dinero" y un desarrollador que pueda decir qué toca el arreglo. Las dos con un teléfono que conteste el viernes por la mañana.
  3. Arregla sobre una copia. Haz el cambio en un duplicado del tema en vivo, o en una rama subida como tema sin publicar, nunca en el editor del tema en vivo.
  4. Smoke test antes. Corre el workflow a mano con el ID del tema en vista previa. Si falla, el arreglo no sale.
  5. Publica en la hora más tranquila. Tu analítica muestra qué hora del día tiene menos sesiones. Nunca durante el envío de un correo de campaña.
  6. Smoke test después. Córrelo otra vez contra la tienda en vivo, sin ID. Después haz la única revisión manual que reproduce el reporte original.
  7. Revierte si cualquiera de las dos falla. Vuelve a publicar el tema de respaldo que guardaste intacto en la biblioteca. Para una app, desactiva su embed o su extensión. Para un descuento, desactívalo. Decide la reversión antes de publicar, no después.
  8. Regístralo. Qué cambió, quién lo aprobó, cuándo se publicó y los dos resultados del smoke test. El 2 de diciembre este registro es la revisión.

La reversión depende de tener el tema de respaldo en la biblioteca. Según la ayuda de Shopify sobre duplicar temas, una tienda guarda 20 temas, y si estás en el límite tienes que borrar uno antes de duplicar. Revisa la cuenta antes del congelamiento, no durante un incidente.

La lista para la semana antes de Black Friday

  • La fecha, la hora y la zona horaria del congelamiento están en el calendario compartido, y cada agencia y freelance con acceso a la tienda la confirmó por escrito.
  • Los dos aprobadores están nombrados, con teléfono, y los dos conocen la definición de urgente.
  • El tema en vivo tiene un duplicado con fecha y sin tocar en la biblioteca de temas, y hay espacio para un tema más.
  • El smoke test está en verde contra la tienda en vivo, programado cada hora, y una ejecución fallida avisa a una persona.
  • El producto del smoke test tiene stock suficiente y no forma parte de un cambio de precio.
  • El modo de prueba de pagos está apagado en la tienda en vivo y la pasarela de pago de prueba no está activa.
  • Cada descuento de Black Friday se probó una vez en un carrito, en un teléfono, con las combinaciones que va a encontrar. El método completo está en el artículo de QA previo al lanzamiento.
  • Agregar al carrito responde rápido en un teléfono de gama media. Si no, esta guía sobre el botón de agregar al carrito lento explica cómo encontrar el script responsable, y ese arreglo tiene que salir antes de BF−14, no durante el congelamiento.
  • Hay una lista de cada app que toca la tienda o el checkout, con quién es responsable y cómo se desactiva en un paso.
  • Sabes cuál es la hora más tranquila del día para publicar un arreglo urgente.
  • Existe la lista para el 2 de diciembre, así que las peticiones no urgentes tienen a dónde ir.

Nada de esto es mucho trabajo. La prueba es un archivo, el workflow es otro, y el congelamiento es una fecha y un párrafo que todos aceptan. Si quieres ayuda para montarlo en tu tienda, es parte de nuestro trabajo de desarrollo de temas Shopify: la prueba en tu repositorio, el workflow corriendo y el congelamiento por escrito antes de que noviembre se llene.

Escrito por

Luis Morales

Luis Morales

Desarrollador frontend y Shopify, EcomLabs

@luismoralesdev en Instagram

Publicado el 22 de octubre de 2026 · actualizado el 23 de octubre de 2026

← Blog