Merhabalar.

Önceki 2 yazımda Puppeteer'a giriş yapıp, setup ve teardown durumlarını incelemiştik.

Puppeteer ile bir test yazarken bazı durumlarda web uygulamasına giriş yapılması gerekebilir ve giriş bilgilerinin (örneğin cookie veya token bilgileri) bir yerde saklanıp kullanılması gerekebilir.

Bu yazımda ben bir web sitesine login olup ilgili cookie değerlerini nasıl saklayabileceğimizden bahsedeceğim. Örnek olması açısından Ekşi Sözlük'e giriş yapıp oluşan session'a ait cookie bilgilerini saklayacağız.

Oluşan cookie bilgilerini saklamanın birden fazla yolu vardır. Örneğin dosya sistemine JSON formatında kaydetmek, Redis'e, MongoDb'ye veya herhangi bir ilişkisel veritabanına kaydedip sonrasında buradaki değerlere erişip testlerimize devam edebiliriz. Bu yazıda Redis'e ve dosya sistemine yazmayı örnekleyeceğiz.

Öncelikle aşağıdaki komutlar yardımıyla projemizi oluşturalım.

$ mkdir puppeteer-login-test
$ cd puppeteer-login-test
$ yarn add jest puppeteer jest-puppeteer rimraf
$ mkdir __tests__

Bu komutlar sayesinde ihtiyacımız olan temel paketlerin kurulumunu tamamladık. Artık testlerimizi yazmaya başlayabiliriz.

Cookie Bilgilerini Dosya Sistemine Yazma

Dosya sisteminde çalışabilmek için Node.JS'in fs paketini kullanacağız. fs paketinin writeFileSync ve readFileSync metotlarını kullanarak verileri dosya sisteminde saklayacağız.

O zaman testimizi yazmaya başlayabiliriz. tests klasörünün altına index.test.js isimli bir dosya oluşturalım ve içeriğini aşağıdaki şekilde düzenleyelim.

const fs = require("fs");
const timeout = 30000;
const filename = "cookies.json";

let page;
beforeAll(async () => {
  jest.setTimeout(timeout);

  page = await global.__BROWSER__.newPage();

  let cookies = readCookieFile();

  if (cookies === undefined) {
    await page.goto("https://eksisozluk.com/giris");

    await page.waitForSelector("#username");
    await page.type("#username", "username");

    await page.waitForSelector("#password");
    await page.type("#password", "password");

    await page.$eval("#login-form-container form", form => form.submit());

    await page.waitForSelector(".messages", { visible: true, timeout: 0 });

    cookies = await page.cookies();
    let cookieData = JSON.stringify(cookies);

    fs.writeFileSync(filename, cookieData);
  } else {
    await page.setCookie(...cookies);
  }
});

describe("Login Test", () => {
  test(
    "Home page title",
    async () => {
      await page.goto("https://eksisozluk.com", {
        waitUntil: "domcontentloaded"
      });

      const expectedTitle = "ekşi sözlük - kutsal bilgi kaynağı";
      const title = await page.title();
      expect(title).toBe(expectedTitle);
    },
    timeout
  );
});

function readCookieFile() {
  try {
    let fileContent = fs.readFileSync(filename, "utf-8");
    return JSON.parse(fileContent);
  } catch {
    return undefined;
  }
}

Burada yaptığımız işlemleri kısaca açıklamak gerekirse öncelikle Jest'in beforeAll metodu ile tüm testler çalışmadan önce dosya dizinindeki cookies.json dosyasının içeriğini okumaya çalışıyoruz. Eğer bu dosya var ise JSON.parse metodu ile JSON'a çevirip page.setCookie metodu ile Chromium tarayıcının cookie'lerini düzenliyoruz. Eğer dosya sisteminde cookies.json dosyası bulunmuyorsa Chromium'u öncelikle Ekşi Sözlük'ün giriş sayfasına yönlendiriyoruz. Bu ekranda e-posta adresi ve şifre alanlarını otomatik doldurarak ilgili formu sunucuya post ediyoruz. Eğer başarılı bir şekilde giriş yapılırsa page.cookies metodu yardımıyla oluşan cookie bilgisini alıyoruz ve cookies.json dosyasının içerisine yazıyoruz.

puppeter-login-1

Bu işlem sonucunda testimizin başarıyla tamamlandığını ve testin bulunduğu dizinde cookies.json dosyasının oluştuğunu görmekteyiz. Testi bir daha çalıştırdığımız zaman artık giriş sayfasına değil ana sayfaya yönleneceğiz.

puppeteer-login-2
puppeteer-login-3

Cookie Bilgilerini Redis'e Yazma

Örnek uygulamamızda Redis'e bağlanabilmek için redis isimli NPM paketini kullanacağız. İlgili paketi projemize eklemek için aşağıdaki komutu kullanacağız.

yarn add redis

Eğer elimizde herhangi bir Redis sunucu yok ise Docker üzerinde bir Redis sunucusu oluşturup kullanabiliriz. Örnek uygulamada Redis üzerinde bir Docker sunucu oluşturup kullanacağım. Aşağıdaki komut yardımıyla Redis'i Docker üzerinde oluşturabiliriz.

docker run --name redis -p 6379:6379 -d redis

Bu komutla redis isimli, 6379 portundan gelen istekleri dinleyecek bir container'ı Docker üzerinde oluşturduk.

Bu işlemlerden sonra test dosyamızın içeriğini düzenleyebiliriz. tests klasörünün altındaki index.test.js dosyasının içeriğini aşağıdaki şekilde düzenleyelim.

const redis = require("redis");
const { promisify } = require("util");
const timeout = 30000;
const cookieKey = "PUPPETEER_LOGIN_COOKIE";

let page;
let redisClient;

beforeAll(async () => {
  jest.setTimeout(timeout);

  page = await global.__BROWSER__.newPage();

  redisClient = redis.createClient(6379, "localhost");

  redisClient.on("connect", () => {
    console.info("Connected to Redis server.");
  });

  redisClient.on("error", err => {
    console.error(`Something went wrong. ${err}`);
  });

  const getAsync = promisify(redisClient.get).bind(redisClient);

  try {
    let result = await getAsync(cookieKey);
    if (result == null) {
      await page.goto("https://eksisozluk.com/giris");

      await page.waitForSelector("#username");
      await page.type("#username", "username");

      await page.waitForSelector("#password");
      await page.type("#password", "password");

      await page.$eval("#login-form-container form", form => form.submit());

      await page.waitForSelector(".messages", { visible: true, timeout: 0 });

      cookies = await page.cookies();
      let cookieData = JSON.stringify(cookies);
      redisClient.set(cookieKey, cookieData);
    } else {
      let cookies = JSON.parse(result);
      await page.setCookie(...cookies);
    }
  } catch (err) {
    console.log(err);
    throw err;
  }
});

afterAll(() => {
  if (redisClient !== undefined) {
    redisClient.quit();
  }
});

describe("Login Test", () => {
  test(
    "Home page title",
    async () => {
      await page.goto("https://eksisozluk.com", {
        waitUntil: "domcontentloaded"
      });

      const expectedTitle = "ekşi sözlük - kutsal bilgi kaynağı";
      const title = await page.title();
      expect(title).toBe(expectedTitle);
    },
    timeout
  );
});

Burada yaptıklarımızı adım adım açıklayalım. Öncelikle redis.createClient metodu ile localhost:6379 adresindeki Redis sunucusuna bağlantı gerçekleştiriyoruz. Eğer bağlantı başarılı olursa connect, olmazsa error eventları tetiklenecek. Sonrasında ise getAsync metodu yardımıyla Redis'te PUPPETEER_LOGIN_COOKIE isimli anahtarın olup olmadığına bakıyoruz. Eğer yoksa Chromium'u giriş sayfasına yönlendirip e-posta adresi ve şifre bilgilerini otomatik olarak giriyoruz ve oluşan cookie bilgilerini Redis'te PUPPETEER_LOGIN_COOKIE isimli anahtarda saklıyoruz. Zaten bu anahtar varsa ilgili değeri Redis'ten okuyoruz ve page.setCookie metodu ile Chromium'un cookie değerlerini düzenliyoruz.

Testimizi çalıştırdığımız zaman başarılı bir şekilde giriş sayfasına yönlendiğini ve oluşan cookie bilgilerini Redis'e kaydettiğini görmekteyiz.

puppeteer-login-5

puppeteer-login-4

Testi daha sonra çalıştırdığımız zaman ilgili anahtar Redis'te olduğundan dolayı Chromium giriş sayfası yerine ana sayfaya yönlenecektir.

Dosya sistemi ile yapılan örneğe buradan, Redis ile yapılan örneğe ise buradan erişebilirsiniz.