Add gitlab ci script

Change-Id: I0d4d2a0a934b4a47f0e778fbb83f6307fc0a752a
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..ae9025a
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,113 @@
+# GitLab CI/CD configuration for KorAP E2E Tests
+# This pipeline runs end-to-end tests against a KorAP instance
+
+stages:
+  - test
+
+variables:
+  # Default test configuration
+  KORAP_URL: "https://korap.ids-mannheim.de/"
+  KORAP_QUERIES: 'geht, [orth=geht & tt/p="VVFIN"]'
+  LC_ALL: "C"
+  
+  # Node.js configuration
+  NODE_ENV: "test"
+
+# Main E2E test job
+e2e_tests:
+  stage: test
+  image: node:bullseye
+  
+  # Install system dependencies required for Puppeteer
+  before_script:
+    - apt-get update
+    - apt-get install -y 
+        wget 
+        gnupg 
+        ca-certificates 
+        fonts-liberation 
+        libasound2 
+        libatk-bridge2.0-0 
+        libatk1.0-0 
+        libc6 
+        libcairo2 
+        libcups2 
+        libdbus-1-3 
+        libexpat1 
+        libfontconfig1 
+        libgbm1 
+        libgcc1 
+        libglib2.0-0 
+        libgtk-3-0 
+        libnspr4 
+        libnss3 
+        libpango-1.0-0 
+        libpangocairo-1.0-0 
+        libstdc++6 
+        libx11-6 
+        libx11-xcb1 
+        libxcb1 
+        libxcomposite1 
+        libxcursor1 
+        libxdamage1 
+        libxext6 
+        libxfixes3 
+        libxi6 
+        libxrandr2 
+        libxrender1 
+        libxss1 
+        libxtst6 
+        lsb-release 
+        wget 
+        xdg-utils
+    - npm install
+    
+  script:
+    - echo "Running KorAP E2E tests against $KORAP_URL"
+    - npm run test:ci
+    
+  after_script:
+    - echo "Test execution completed, checking for test results..."
+    - ls -la test-results.xml || echo "No test-results.xml found"
+    
+  # Use CI variables for sensitive data
+  variables:
+    KORAP_LOGIN: $KORAP_USERNAME        # Set this in GitLab CI/CD Variables
+    KORAP_PWD: $KORAP_PASSWORD          # Set this in GitLab CI/CD Variables
+    SLACK_WEBHOOK_URL: $SLACK_WEBHOOK   # Optional: Set for Slack notifications
+    
+  # Generate test artifacts - always keep results even on test failure
+  artifacts:
+    when: always  # Collect artifacts on success, failure, and cancellation
+    reports:
+      junit: test-results.xml  # GitLab will parse this for test reporting
+    paths:
+      - "test-results.xml"      # Keep the raw XML file as well
+      - "failed_*.png"          # Screenshots of failed tests
+    expire_in: 1 week
+    
+  # Retry on failure (network issues, etc.)
+  retry:
+    max: 2
+    when:
+      - unknown_failure
+      - api_failure
+      - runner_system_failure
+      
+  # Pipeline trigger rules
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+    - if: $CI_PIPELINE_SOURCE == "schedule"  # Allow scheduled runs
+    - when: manual
+
+  # Set timeout
+  timeout: 5 minutes
+
+# Optional: Scheduled test job for regular monitoring
+scheduled_tests:
+  extends: e2e_tests
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "schedule"
+  variables:
+    SLACK_WEBHOOK_URL: $SLACK_WEBHOOK  # Enable notifications for scheduled runs
diff --git a/GITLAB_CI_SETUP.md b/GITLAB_CI_SETUP.md
new file mode 100644
index 0000000..3a77789
--- /dev/null
+++ b/GITLAB_CI_SETUP.md
@@ -0,0 +1,166 @@
+# GitLab CI/CD Setup for KorAP E2E Tests
+
+This document explains how to configure GitLab CI/CD variables for running the KorAP E2E tests.
+
+## Required CI/CD Variables
+
+To run the E2E tests in GitLab CI/CD, you need to set up the following variables in your GitLab project:
+
+### 1. Navigate to Project Settings
+- Go to your GitLab project
+- Navigate to **Settings > CI/CD**
+- Expand the **Variables** section
+
+### 2. Add Required Variables
+
+#### KORAP_USERNAME (Required)
+- **Key**: `KORAP_USERNAME`
+- **Value**: Your KorAP login username
+- **Type**: Variable
+- **Environment scope**: All
+- **Protect variable**: ✅ (recommended)
+- **Mask variable**: ❌ (usernames are usually not sensitive)
+
+#### KORAP_PASSWORD (Required)
+- **Key**: `KORAP_PASSWORD`
+- **Value**: Your KorAP login password
+- **Type**: Variable
+- **Environment scope**: All
+- **Protect variable**: ✅ (recommended)
+- **Mask variable**: ✅ (recommended)
+
+#### SLACK_WEBHOOK (Optional)
+- **Key**: `SLACK_WEBHOOK`
+- **Value**: Your Slack webhook URL for notifications
+- **Type**: Variable  
+- **Environment scope**: All
+- **Protect variable**: ✅ (recommended)
+- **Mask variable**: ✅ (recommended)
+
+## Optional Configuration Variables
+
+You can override the default configuration by setting these variables:
+
+#### KORAP_URL
+- **Default**: `https://korap.ids-mannheim.de/`
+- **Description**: The KorAP instance URL to test against
+
+#### KORAP_QUERIES
+- **Default**: `geht, [orth=geht & tt/p="VVFIN"]`
+- **Description**: Comma-separated list of queries to test
+
+## Pipeline Triggers
+
+The CI pipeline will run:
+- ✅ On pushes to the main branch
+- ✅ On merge requests
+- ✅ On scheduled pipelines (if configured)
+- ✅ On manual triggers (web UI or API)
+
+## Manual Triggering
+
+You can manually trigger the E2E tests in several ways:
+
+### 1. Via GitLab Web UI
+1. Go to your project in GitLab
+2. Navigate to **CI/CD > Pipelines**
+3. Click **Run pipeline**
+4. Select the branch you want to test
+5. Optionally add custom variables (see below)
+6. Click **Run pipeline**
+
+### 2. Via GitLab API
+
+First, create a pipeline trigger token:
+1. Go to **Settings > CI/CD > Pipeline triggers**
+2. Click **Add trigger**
+3. Give it a description (e.g., "Manual E2E tests")
+4. Copy the generated token
+
+Then use the API:
+```bash
+curl -X POST \
+  -F token=<your-trigger-token> \
+  -F ref=main \
+  -F "variables[KORAP_URL]=https://korap.ids-mannheim.de/" \
+  -F "variables[KORAP_QUERIES]=geht" \
+  https://gitlab.example.com/api/v4/projects/<project-id>/trigger/pipeline
+```
+
+### 3. Custom Variables for Manual Runs
+When manually triggering, you can override default values by adding variables:
+
+- **KORAP_URL**: Test against a different KorAP instance
+- **KORAP_USERNAME**: Use a different username  
+- **KORAP_QUERIES**: Test with custom queries
+- **DEBUG**: Set to `puppeteer:*` for verbose output
+
+Example: To test against a local instance:
+- Key: `KORAP_URL`, Value: `http://localhost:64543`
+- Key: `KORAP_USERNAME`, Value: `testuser`
+
+## Test Artifacts
+
+When tests run, the pipeline will **always** generate artifacts, regardless of test success or failure:
+
+- **JUnit XML report**: `test-results.xml` for GitLab test reporting and analysis
+- **Screenshots**: `failed_*.png` files for any failing tests  
+- **Artifacts retention**: 1 week
+
+The artifacts are collected with `when: always`, ensuring that:
+- ✅ Test results are preserved even when tests fail
+- ✅ Failure screenshots are captured for debugging
+- ✅ GitLab can display test reports in the merge request and pipeline views
+- ✅ You can download and analyze the detailed XML test results
+
+## Scheduling Tests
+
+To run tests on a schedule:
+
+1. Go to **CI/CD > Schedules**
+2. Click **New schedule**
+3. Set your desired frequency (e.g., daily, weekly)
+4. The scheduled runs will include Slack notifications if configured
+
+## Local Testing
+
+To test the same configuration locally:
+
+```bash
+export KORAP_USERNAME="your-username"
+export KORAP_PASSWORD="your-password"
+export KORAP_URL="https://korap.ids-mannheim.de/"
+export KORAP_QUERIES='geht, [orth=geht & tt/p="VVFIN"]'
+export LC_ALL="C"
+
+npm test
+```
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Missing required variables**
+   - Ensure both `KORAP_USERNAME` and `KORAP_PASSWORD` variables are set
+   - Check that variables are not masked incorrectly
+   - Verify that the variable scope includes your branch
+
+2. **Puppeteer installation issues**
+   - The CI configuration includes all required system dependencies
+   - Uses Node.js 18 with Debian Bullseye for compatibility
+
+3. **Chrome sandbox errors in CI**
+   - Error: "Running as root without --no-sandbox is not supported"
+   - Solution: The test configuration includes `--no-sandbox` and other CI-friendly Chrome flags
+   - These flags are automatically applied in the test setup
+
+4. **Test timeouts**
+   - Pipeline timeout is set to 5 minutes
+   - Individual tests have their own timeouts (15-20 seconds)
+   - Network issues are handled with automatic retry (max 2 attempts)
+
+### Debugging
+
+Enable debug output by adding this variable:
+- **Key**: `DEBUG`
+- **Value**: `puppeteer:*`
diff --git a/Readme.md b/Readme.md
index 1c133ab..93bc538 100644
--- a/Readme.md
+++ b/Readme.md
@@ -30,6 +30,15 @@
 - Use `KORAP_LOGIN="" npm test` to skip login and logout tests, e.g. to run tests against Kustvakt-lite.
 - The tests respect the current locale, consider e.g. `LC_ALL=C npm test`
 
+## GitLab CI/CD
+
+This project includes GitLab CI/CD configuration for automated testing. See [GITLAB_CI_SETUP.md](GITLAB_CI_SETUP.md) for detailed setup instructions.
+
+Quick setup:
+1. Set the `KORAP_PASSWORD` variable in your GitLab project's CI/CD settings
+2. Optionally set `SLACK_WEBHOOK` for notifications
+3. Push to trigger the pipeline
+
 #### Notifications
 
 If you run KorAP-E2E-tests as a cronjob or in scheduled pipelines and
diff --git a/package-lock.json b/package-lock.json
index c2b594c..a3b63ab 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
       "dependencies": {
         "chai": "^4.4.1",
         "mocha": "^10.6.0",
+        "mocha-junit-reporter": "^2.2.1",
         "puppeteer": "^23.3.0",
         "puppeteer-extra": "^3.3.6",
         "puppeteer-extra-plugin-user-preferences": "^2.4.1",
@@ -487,6 +488,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/check-error": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
@@ -613,6 +623,15 @@
         }
       }
     },
+    "node_modules/crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/data-uri-to-buffer": {
       "version": "6.0.2",
       "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
@@ -1361,6 +1380,17 @@
         "node": ">=12"
       }
     },
+    "node_modules/md5": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz",
+      "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "~1.1.6"
+      }
+    },
     "node_modules/merge-deep": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz",
@@ -1415,6 +1445,21 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/mkdirp": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+      "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+      "license": "MIT",
+      "bin": {
+        "mkdirp": "dist/cjs/src/bin.js"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/mocha": {
       "version": "10.8.2",
       "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz",
@@ -1450,6 +1495,22 @@
         "node": ">= 14.0.0"
       }
     },
+    "node_modules/mocha-junit-reporter": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/mocha-junit-reporter/-/mocha-junit-reporter-2.2.1.tgz",
+      "integrity": "sha512-iDn2tlKHn8Vh8o4nCzcUVW4q7iXp7cC4EB78N0cDHIobLymyHNwe0XG8HEHHjc3hJlXm0Vy6zcrxaIhnI2fWmw==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.3.4",
+        "md5": "^2.3.0",
+        "mkdirp": "^3.0.0",
+        "strip-ansi": "^6.0.1",
+        "xml": "^1.0.1"
+      },
+      "peerDependencies": {
+        "mocha": ">=2.2.5"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.3",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -2271,6 +2332,12 @@
         }
       }
     },
+    "node_modules/xml": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz",
+      "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==",
+      "license": "MIT"
+    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
diff --git a/package.json b/package.json
index ba20847..e9afa5c 100644
--- a/package.json
+++ b/package.json
@@ -12,16 +12,18 @@
     "lib": "lib"
   },
   "scripts": {
-    "test": "mocha"
+    "test": "mocha",
+    "test:ci": "mocha --reporter mocha-junit-reporter --reporter-options mochaFile=test-results.xml"
   },
   "author": "Marc Kupietz",
   "license": "APACHE-2.0",
   "dependencies": {
     "chai": "^4.4.1",
     "mocha": "^10.6.0",
+    "mocha-junit-reporter": "^2.2.1",
     "puppeteer": "^23.3.0",
     "puppeteer-extra": "^3.3.6",
     "puppeteer-extra-plugin-user-preferences": "^2.4.1",
     "slack-notify": "^2.0.7"
   }
-}
+}
\ No newline at end of file
diff --git a/test/korap-ui.js b/test/korap-ui.js
index d0dbb77..2c42858 100644
--- a/test/korap-ui.js
+++ b/test/korap-ui.js
@@ -14,8 +14,8 @@
 var slack = null;
 
 const KORAP_URL = process.env.KORAP_URL || "http://localhost:64543";
-const KORAP_LOGIN = 'KORAP_LOGIN' in process.env ? process.env.KORAP_LOGIN : "user2"
-const KORAP_PWD = process.env.KORAP_PWD || "password2";
+const KORAP_LOGIN = 'KORAP_USERNAME' in process.env ? process.env.KORAP_USERNAME : 'KORAP_LOGIN' in process.env ? process.env.KORAP_LOGIN : "user2"
+const KORAP_PWD = process.env.KORAP_PWD || process.env.KORAP_PASSWORD || "password2";
 const KORAP_QUERIES = process.env.KORAP_QUERIES || 'geht, [orth=geht & cmc/pos=VVFIN]'
 const korap_rc = require('../lib/korap_rc.js').new(KORAP_URL)
 
@@ -33,7 +33,16 @@
 
     before(async () => {
         browser = await puppeteer.launch({
-            headless: "shell"
+            headless: "shell",
+            args: [
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-accelerated-2d-canvas',
+                '--no-first-run',
+                '--no-zygote',
+                '--disable-gpu'
+            ]
         })
         page = await browser.newPage()
         await page.setViewport({