edulution-io / edulution-ui

https://edulution.io/
2 stars 0 forks source link

Find a way to automatically test code injection in native embedded apps #193

Open TomlDev opened 1 week ago

TomlDev commented 1 week ago

Currently we have for example the SOGo application embedded as native frame with SSO code injection.

If we keep this instead of implementing a cookie based SSO we need to add tests to check the script injection.

It could be possible to test the component with static HTML code or a proxied SOGo.

The SSO injections have shown again that it is a really fragile system, which is why I would now take on the task of testing it automatically. My current idea would be via Github Actions. You would need a configuration for each embedded page (credentials, URL, path to the login/logout script). Set up a reverse proxy (probably traefik) in the action, preferably with our existing proxy configuration as a basis and the special values of the embedded page (which we then use to derive the supported version for the Edulution documentation).

For the embedded app, we use the pages we are working with (e.g. those from the demo instance). Or just in the action in the Docker would also be an option, but I think using an existing one is easier because of the setup and, above all, the test is faster.

Then there are so-called headless browser automation tools such as puppteer/playwright which can also open iframes via which we can then inject scripts and then check whether the elements we expect are displayed on the page (to detect whether login/logout have worked).

TomlDev commented 1 week ago

Your goal is to automate testing for the JavaScript injection in iframes (embedded apps) for authentication when using React, and you'd like to trigger these tests with GitHub Actions, especially after a PR merge. Here's a structured approach to implement this:

1. Set Up the Proxy (Traefik or Another Solution)

To solve the cross-origin issue that arises from embedding external pages within iframes and injecting JavaScript, setting up a reverse proxy like Traefik is a great solution. This will allow your embedded app to appear as though it’s coming from the same domain as your main app, thus allowing JavaScript injection.

This proxy will help with bypassing CORS and same-origin policy restrictions during the testing phase.

2. Create Configuration Files for Embedded Pages

Each embedded page likely has different credentials, URLs, and login/logout flows. A configuration file per embedded app allows easy updating of the test logic and management. You can use JSON or YAML for these configuration files:

Example config.json:

{
  "embeddedApps": [
    {
      "name": "App1",
      "loginUrl": "https://app1.example.com/login",
      "logoutUrl": "https://app1.example.com/logout",
      "credentials": {
        "username": "testUser",
        "password": "testPassword"
      },
      "scripts": {
        "loginScript": "path/to/loginScript.js",
        "logoutScript": "path/to/logoutScript.js"
      },
      "successSelectors": {
        "login": "#loggedInElement",
        "logout": "#loggedOutElement"
      }
    },
    {
      "name": "App2",
      "loginUrl": "https://app2.example.com/login",
      "logoutUrl": "https://app2.example.com/logout",
      "credentials": {
        "username": "testUser2",
        "password": "testPassword2"
      },
      "scripts": {
        "loginScript": "path/to/loginScript2.js",
        "logoutScript": "path/to/logoutScript2.js"
      },
      "successSelectors": {
        "login": ".loginSuccess",
        "logout": ".logoutSuccess"
      }
    }
  ]
}

This config can be parsed in your tests to dynamically load credentials, URLs, and JavaScript files.

3. Automated Testing with Headless Browser

To automate testing, use a headless browser tool like Puppeteer or Playwright. These tools allow you to simulate user interactions, inject scripts, and validate the state of the iframe content. Here’s an outline:

Puppeteer Example:

  1. Install Puppeteer:

    npm install puppeteer
  2. Automate Login/Logout Test:

    const puppeteer = require('puppeteer');
    const config = require('./config.json');  // Load the configuration file
    
    (async () => {
     const browser = await puppeteer.launch({ headless: true });
     const page = await browser.newPage();
    
     for (const app of config.embeddedApps) {
       await page.goto(app.loginUrl);
    
       // Inject login script
       await page.addScriptTag({ path: app.scripts.loginScript });
    
       // Wait for login to succeed (using a success selector)
       await page.waitForSelector(app.successSelectors.login);
    
       console.log(`${app.name}: Login successful`);
    
       // Perform the logout process
       await page.goto(app.logoutUrl);
       await page.addScriptTag({ path: app.scripts.logoutScript });
    
       // Wait for logout to succeed
       await page.waitForSelector(app.successSelectors.logout);
    
       console.log(`${app.name}: Logout successful`);
     }
    
     await browser.close();
    })();
  3. Integrate into GitHub Actions: Create a .github/workflows/test.yml file to automate the test when you merge a PR.

Example GitHub Actions workflow:

name: Embedded App Login/Logout Test

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14'

      - name: Install dependencies
        run: npm install

      - name: Run Puppeteer Tests
        run: node testEmbeddedApps.js  # The Puppeteer script file

4. Validation of Script Success

To check if the JavaScript injection was successful, use the following approaches:

Example: Capturing a Response

page.on('response', response => {
  if (response.url().includes('login-success-endpoint')) {
    console.log('Login API returned success');
  }
});

5. Triggering Tests on PR Merges

The GitHub Actions workflow is designed to trigger automatically on PR merges into the main branch. This ensures the tests are run each time new changes are introduced, helping to catch issues early.

Summary

This solution should give you robust, automated tests that ensure your iframe JavaScript injection is always working correctly.