Playwright testing with JanusGraph

Pawan GangwaniPawan Gangwani
4 min read

This article covers how to connect to multiple JanusGraph instances in Playwright using TypeScript, manage connections securely with environment variables, and utilize custom fixtures for setup and teardown.

Prerequisites

  • Node.js and TypeScript installed.

  • Playwright and Gremlin client libraries installed.

  • Access to JanusGraph instances with a JKS truststore certificate (converted to PEM format).

  • Environment variables setup (use .env files if needed).

1. Install Required Dependencies

Install Playwright, Gremlin client, and TypeScript typings:

npm install playwright gremlin @types/gremlin

2. Set Up Environment Variables

Define sensitive data like the username and password in a .env file in the project root. You may want to use different credentials for each JanusGraph instance if needed.

Create a .env file:

JANUSGRAPH1_USERNAME=yourUsername1
JANUSGRAPH1_PASSWORD=yourPassword1
JANUSGRAPH2_USERNAME=yourUsername2
JANUSGRAPH2_PASSWORD=yourPassword2

Load these environment variables into your project with dotenv:

npm install dotenv

Add the following at the top of your test file to load environment variables:

import * as dotenv from 'dotenv';
dotenv.config();

3. Converting JKS to PEM Format

Follow these steps to convert your JKS file to a PEM certificate, required by Node.js:

  1. Use keytool to export from JKS to DER:

     keytool -exportcert -alias your-alias -keystore your-truststore.jks -file cert.der
    
  2. Convert the .der file to PEM using OpenSSL:

     openssl x509 -inform der -in cert.der -out cert.pem
    

4. Configure Playwright with Multiple Connections

playwright.config.ts

import { defineConfig } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'JanusGraph Tests - Instance 1',
      testDir: './tests',
      workers: 1,
    },
    {
      name: 'JanusGraph Tests - Instance 2',
      testDir: './tests',
      workers: 1,
    },
  ],
});

tests/janusgraph.spec.ts

The following setup defines a custom fixture to connect to multiple JanusGraph instances using different credentials, with credentials loaded from environment variables.

import { test as base, expect } from '@playwright/test';
import gremlin from 'gremlin';
import * as fs from 'fs';
import * as dotenv from 'dotenv';

dotenv.config(); // Load environment variables

// Define a custom type for multiple connections
type JanusConnections = {
  g1: gremlin.process.GraphTraversalSource;
  g2: gremlin.process.GraphTraversalSource;
};

// Extend Playwright's base test with the fixture
export const test = base.extend<{ janusConnections: JanusConnections }>({
  janusConnections: async ({}, use) => {
    const { DriverRemoteConnection } = gremlin.driver;
    const { Graph } = gremlin.structure;

    // Load PEM certificate for both connections
    const caCert = fs.readFileSync('path/to/cert.pem');

    // Establish a connection for JanusGraph instance 1
    const conn1 = new DriverRemoteConnection(
      'wss://janusgraph-instance-1-endpoint',
      {
        mimeType: 'application/json',
        headers: {
          Authorization:
            'Basic ' +
            Buffer.from(
              `${process.env.JANUSGRAPH1_USERNAME}:${process.env.JANUSGRAPH1_PASSWORD}`
            ).toString('base64'),
        },
        ca: [caCert],
      }
    );

    // Establish a connection for JanusGraph instance 2
    const conn2 = new DriverRemoteConnection(
      'wss://janusgraph-instance-2-endpoint',
      {
        mimeType: 'application/json',
        headers: {
          Authorization:
            'Basic ' +
            Buffer.from(
              `${process.env.JANUSGRAPH2_USERNAME}:${process.env.JANUSGRAPH2_PASSWORD}`
            ).toString('base64'),
        },
        ca: [caCert],
      }
    );

    // Create graph traversal sources
    const graph1 = new Graph();
    const g1 = graph1.traversal().withRemote(conn1);

    const graph2 = new Graph();
    const g2 = graph2.traversal().withRemote(conn2);

    // Provide both connections to tests
    await use({ g1, g2 });

    // Teardown: Close both connections after tests
    await conn1.close();
    await conn2.close();
  },
});

// Example test using both JanusGraph connections
test('should fetch vertices from both JanusGraph instances', async ({
  janusConnections,
}) => {
  const { g1, g2 } = janusConnections;

  // Perform read-only query on instance 1
  const vertices1 = await g1.V().toList();
  expect(vertices1.length).toBeGreaterThan(0);

  // Perform read-only query on instance 2
  const vertices2 = await g2.V().toList();
  expect(vertices2.length).toBeGreaterThan(0);
});

Explanation

  1. Environment Variables:

    • Environment variables store sensitive credentials and are accessed using process.env.
  2. Multiple Connections:

    • g1 and g2 represent two different JanusGraph connections, each with unique credentials and endpoints.

    • Separate DriverRemoteConnection instances handle each connection’s setup, ensuring no interference between them.

  3. Reusable Fixture:

    • The janusConnections fixture is shared across tests and closes connections in the teardown phase, maintaining resource efficiency.

Running the Tests

To run the tests, execute:

npx playwright test

Notes

  • Error Handling: In production, consider adding error handling around the connection to manage network or authentication issues.

  • Environment Management: Consider using different .env files or configuration files for various environments (e.g., development, staging, production).


0
Subscribe to my newsletter

Read articles from Pawan Gangwani directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Pawan Gangwani
Pawan Gangwani

I’m Pawan Gangwani, a passionate Full Stack Developer with over 12 years of experience in web development. Currently serving as a Lead Software Engineer at Lowes India, I specialize in modern web applications, particularly in React and performance optimization. I’m dedicated to best practices in coding, testing, and Agile methodologies. Outside of work, I enjoy table tennis, exploring new cuisines, and spending quality time with my family.