Bir süredir bu konu ile alakalı bir şeyler denemek ve karalamak istiyordum fakat fırsat bulamamıştım.  Puppeteer, google tarafından geliştirilen chrome’u headless (herhangi bir kullanıcı arayüzü olmadan) olarak yönetebileceğiniz api sunan bir nodejs kütüphanesidir.

puppeteer.js

Tarayıcınızda manuel olarak yapabildiğiniz hemen her şeyi puppeteer ile otomatikleştirebilirsiniz.

Bir kaç örnek vermek gerekirse;

  • Sayfaların istediğiniz çözünürlükte ekran görüntülerini alabilir ve pdf olarak kaydedebilir,
  • Web sayfalarından programatik şekilde veri çekebilir,
  • Her deploy öncesi yapılan sıkıcı son kullanıcı testlerinizi otomatikleştirebilirsiniz…

Bu yazıda basit bir todo (https://github.com/irfansimsar/es6-todo-app) uygulaması üzerinden son kullanıcı testlerini nasıl otomatikleştirebileceğimize bakacağız. Daha önce bu tip bir işle uğraştıysanız muhtemelen phantomjs ve selenium gibi diğer popüler alternatiflerle çalışmışsınızdır.  Puppeteer’ın en sevdiğim yanı çok kolay öğrenilebilir ve hızlı şekilde kullanılabiliyor olması.

Puppeteer’ı aşağıdaki şekilde başka bir eklentiye gerek duymadan aşağıdaki gibi kullanabiliriz.

import puppeteer from 'puppeteer';

(async () => {
  // Puppeteer ile browser'ı aç
  const browser = await puppeteer.launch();

  // Browser'da yeni bir sayfa aç
  const page = await browser.newPage();

  // Test edilecek sayfaya git
  await page.goto('http://loremipsum.com');

  // Placeholder'ı "Kullanıcı adı" olan inputu görene kadar bekle
  await page.waitForSelector('input[placeholder="Kullanıcı adı"]');

  // Placeholder'ı "Kullanıcı adı" olan input'a kullanıcı adını gir
  await page.type('input[placeholder="Kullanıcı adı"]', 'lorem');

  // Placeholder'ı "Şifre" olan input'a şifreyi gir
  await page.type('input[placeholder="Şifre"]', "ipsum");

  // "submit-button" class'ına sahip olan butona tıkla
  await page.click('.submit-button');

  // 2 saniye bekle
  await page.waitFor(2000);

  // Giriş yapıldı mı kontrolü yap ve console'a bas
  await page.evaluate(() => {
    if (page.$('input[placeholder="Kullanıcı adı"]')) {
      console.log('Giriş yapılamadı');
    } else {
      console.log('Giriş yapıldı');
    }
  });

  // Açılan sayfanın ekran görüntüsünü al example.png olarak kaydet
  await page.screenshot({path: 'example.png'});

  // Tarayıcıyı kapat
  await browser.close();
})();

Fakat biz bu örnekte biraz daha gerçek dünya problemi çözdüğümüzü düşünerek Jest (Facebook’un geliştirdiği test framework’ü) ve Puppeteer’ı birlikte kullanacağız.

Koda girişmeden önce projemizin (https://irfansimsar.github.io/es6-todo-app/) ne iş yaptığına bakalım ve basit olarak bir kaç test case’i yazalım.

puppeteer.js

Kullanıcı sayfadaki input field’ına minimum 3 karakterden oluşacak şekilde görev girer enter’a basar ve girdiği görevi input field’ının altındaki listede görür.

Daha önce oluşturduğu görev üzerindeki “👌” butonuna tıklayarak ilgili görevi tamamlandı olarak işaretleyebilir. Tamamlanmış görev listede üzeri çizili şekilde görünür.

Daha önce oluşturduğu görev üzerindeki “👎” butonuna tıklayarak ilgili görevi silebilir. Silinen görev listeden kalkar.

  1. Öncelikle terminal’imizi açıyoruz ve testlerimizi yazacağımız bir test klasörü oluşturuyoruz ve içerisine giriyoruz.
$ mkdir test
$ cd test
  1. Npm üzerinden puppeteer ve jest’i yüklüyoruz.
$ npm i puppeteer jest --save-dev
  1. Test folder’ı içerisine testlerimizi yazacağımız js dosyasını oluşturuyoruz.
$ mkdir test
$ touch test/uitest.spec.js
  1. Ve test kodumuzu yazıyoruz
const puppeteer = require('puppeteer');

const appUrl = 'https://irfansimsar.github.io/es6-todo-app';
let page;
let browser;
const width = 800;
const height = 600;
const testTasks = [
  "Lorem ipsum",
  "do"
];

beforeAll(async () => {
  browser = await puppeteer.launch({
    args: [`--window-size=${width},${height}`]
  });
  page = await browser.newPage();
  await page.setViewport({ width, height });
  await page.goto(appUrl);
});

afterAll(() => {
  browser.close();
});

describe('Add new task', () => {
  test('user can add new task minimum 3 char', async () => {
    await page.waitForSelector('input[placeholder="please enter the task..."]');
    const inputElem = await page.$('input[placeholder="please enter the task..."]');
    const beforeTodoCount = await page.$$eval('.todos li', li => li.length);
    await inputElem.type(testTasks[0]);
    await inputElem.press('Enter');
    const afterTodoCount = await page.$$eval('.todos li', li => li.length);

    expect(afterTodoCount).toBeGreaterThan(beforeTodoCount);
  });

  test('user can not add new task if character length less than 3', async () => {
    await page.waitForSelector('input[placeholder="please enter the task..."]');
    const inputElem = await page.$('input[placeholder="please enter the task..."]');
    const beforeTodoCount = await page.$$eval('.todos li', li => li.length);
    await inputElem.type(testTasks[1]);
    await inputElem.press('Enter');
    const afterTodoCount = await page.$$eval('.todos li', li => li.length);

    expect(afterTodoCount).toBe(beforeTodoCount);
  });
});

describe('Mark a task as completed', () => {
  test('user can mark a task as completed', async () => {
    const btnDone = await page.$('.todos li .btn-done');
    await btnDone.click();
    const doneCount = await page.$$eval('.todos li.done', li => li.length);
    
    expect(doneCount).toBe(1);
  });
});


describe('Delete a task', () => {
  test('user can delete a task', async () => {
    const btnDone = await page.$('.todos li .btn-delete');
    await btnDone.click();
    const afterTodoCount = await page.$$eval('.todos li', li => li.length);

    expect(afterTodoCount).toBe(0);
  });
});

Daha detaylı bilgi ve örnekler için github sayfasını inceleyebilirsiniz.