Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: cannot connect to secure web socket server spun up during test run #143

Open
GrayedFox opened this issue May 4, 2023 · 2 comments
Open

Comments

@GrayedFox
Copy link

GrayedFox commented May 4, 2023

Details

There is no documented wsLocalSuport capability on the PlayWright page: https://www.browserstack.com/docs/automate/playwright/playwright-capabilities

I am having a similar issue to #106. Our test suite uses the ws package to spin up a web socket server which our AUT connects to when under test.

Everything works fine locally, and I have followed the advice on that thread, but still get the following error inside BrowserStack logs:

ERROR: WebSocket connection to 'wss://bs-local.com:12516/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
ERROR: CloseEvent

Here is the web socket and server implementation:

import { Page } from '@playwright/test';
import fs from 'fs';
import https from 'https';
import { Mockmock } from 'mockmock';
import WebSocket, { WebSocketServer } from 'ws';

import { ClientData } from '../test-data';
import { isBrowserStack } from './browserstack';

const MM = Mockmock.instance;

/**
 * Sets up a dummy WebSocket server that responds with web socket data
 */
const setUpWebSocketServer = (port: number, socketId: string) => {
  const server = https.createServer({
    cert: fs.readFileSync('playwright/support/certs/cert.pem'),
    key: fs.readFileSync('playwright/support/certs/key.pem'),
  });

  server.listen(port, () => {
    console.log(`HTTPS server listening on port ${port}`);
  });

  const wss = new WebSocketServer({ server, verifyClient: () => true });

  // replays mocked data every 250ms
  wss.on('connection', (ws: WebSocket) => {
    // recursively replays mocked data until we run out of msgs
    const recursivelySendData = () => {
      const frame = MM.replay(socketId, 'data');
      if (typeof frame === 'undefined') {
        return;
      }
      ws.send(JSON.stringify(frame.mock));
      setTimeout(recursivelySendData, 250);
    };
    recursivelySendData();

    ws.on('error', (err) => {
      console.error(`Connetion error: ${err.message} with name ${err.name}:
      ${err.stack}`);
    });
  });
};

/**
 * Here we link PlayWright logic and MockMock by intercepting all WebSocket requests.
 * This is called by the setUp() method.
 *
 * @param page the page instance
 * @param port defaults to 3030
 * @param url defaults to appropriate environment websocket Url
 */
export const interceptWebSocketRequests = async (
  page: Page,
  port = 3030,
  url = ClientData.websocketUrl
) => {
  const socketId = 'wsData';
  const host = isBrowserStack() ? 'bs-local.com' : 'localhost';
  const wssUrl = MM.isReplaying ? `wss://${host}:${port}` : url;

  // if Mockmock is recording sniff all websocket frames and save them
  if (MM.isRecording) {
    page.on('websocket', (ws) => {
      ws.on('framereceived', (data) => {
        MM.record(socketId, JSON.parse(data.payload.toString()));
      });
    });
  }

  // if Mockmock replaying we set up a local webwocket server and route traffic to it
  if (MM.isReplaying) {
    // intercept the payments form, replace the wss url, and relax CS policies
    await page.route(`${ClientData.paymentsUrl}/**`, async (route) => {
      console.log(
        `Intercepted payments URL request to ${route.request().url()}`
      );
      const response = await route.fetch();
      const frameHtml = await response.text();
      const body = frameHtml.replace(ClientData.websocketUrl, wssUrl);
      const headers = response.headers();
      const csp = headers['content-security-policy'];

      // make csp more permissive
      headers['content-security-policy'] = csp.replace(
        "connect-src 'self'",
        "connect-src 'self' localhost:* wss://localhost:* bs-local.com:* wss://bs-local.com:*"
      );

      console.log(`Fulfilling with
      wss url: ${wssUrl}
      edited body has wss url: ${body.includes(wssUrl)}
      headers: ${JSON.stringify(headers)}
      `);

      await route.fulfill({ response, body, headers });
    });

    setUpWebSocketServer(port, socketId);
  }
};

Expected Behavior

The AUT, when it submits the payment form, should connect to the local secure web socket server and work the same way it does when running the tests locally.

Actual Behavior

The web socket server errors when a connection attempt is made.

ERROR: WebSocket connection to 'wss://bs-local.com:46300/' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
ERROR: CloseEvent

Steps to Reproduce the Problem

  1. Create a test file that acts as an AUT that connects to the secure web socket server - I can't share our application code as it's IP, but that part shouldn't be too hard.

  2. Write a test that triggers the AUT connecting to your ws secure web socket server.

Platform details

  1. playwright version: 1.33.0
  2. node version: 18.9.1
  3. os type and version: Ubuuntu 22.04.2 LTS
@sharutkarsh
Copy link

Hi @GrayedFox
Kindly please raise a support ticket by sending an email to [email protected] or via Contact Us here.

@olekach
Copy link

olekach commented Oct 4, 2024

@GrayedFox Hi! Is await page.route(frameUrl, async (route) => { still working for you? For me, it is ignoring any wss urls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants