---
- name: โ
Ensure the pass reports dir exists
ansible.builtin.file:
path: "{{ _playwright_pass_reports_dir }}"
state: directory
mode: "0755"
- name: ๐ Render .env from templates/playwright.env.j2
ansible.builtin.copy:
dest: "{{ _playwright_stage_dir }}/.env"
mode: "0644"
content: |
{% set _env = lookup('template', _playwright_env_template, template_vars={'domain': _playwright_pass_domain}) %}
{% if _playwright_pass_domain == _playwright_passes[0] %}
{{ _env }}
{% else %}
{% for line in _env.splitlines() %}
{{ line if not line.startswith('CANONICAL_DOMAIN=')
else 'CANONICAL_DOMAIN=' ~ (lookup('network_siblings', line.split('=', 1)[1] | replace('"', ''))
| in_network(_playwright_pass_domain | network_of) | first | dotenv_quote) }}
{% endfor %}
{% endif %}
{% for name, value in TEST_E2E_PLAYWRIGHT_CONFIG_TIMEOUTS.items() %}
{{ name }}={{ value }}
{% endfor %}
PLAYWRIGHT_NETWORK_LEAK={{ (_playwright_passes | length > 1) | string | lower }}
PLAYWRIGHT_NETWORK_SUFFIXES={{ _playwright_passes | map('network_of') | network_suffixes | to_json }}
PLAYWRIGHT_NETWORK_ALLOWED_HOSTS={{ OIDC.URL | urlsplit('hostname') }}
{% set _bypass = lookup('browser_proxy_bypass', _playwright_pass_domain | network_of, OIDC.URL | urlsplit('hostname')) %}
{% if _bypass %}
PLAYWRIGHT_PROXY_BYPASS={{ _bypass | join(',') }}
{% endif %}
vars:
application_id: "{{ application_id }}"
domains: "{{ lookup('domains') }}"
- name: โณ Wait until application is ready (any non-error response)
uri_retry:
url: "{{ lookup('tls', _playwright_pass_domain, 'url.base') }}"
status_code: "{{ range(100, 500) | list }}"
validate_certs: false
follow_redirects: none
register: _wait_result
retries: "{{ test_e2e_playwright_wait_retries }}"
delay: "{{ test_e2e_playwright_wait_delay }}"
until: >-
_wait_result.status is defined and
_wait_result.status | int >= 100 and
_wait_result.status | int < 500
when:
- TEST_E2E_PLAYWRIGHT_WAIT_ENABLED | bool
- not (TEST_E2E_PLAYWRIGHT_IS_ONION | bool)
- lookup('config', application_id, 'services.frontend.enabled', true) | bool
- name: โณ Wait until the onion answers over Tor (recreate circuits on exhaustion)
ansible.builtin.script: >-
{{ role_path }}/files/wait_onion_reachable.sh
{{ lookup('tor_socks') | quote }}
{{ _playwright_pass_domain | quote }}
{{ TEST_E2E_PLAYWRIGHT_ONION_PROBE_RETRIES | int }}
{{ TEST_E2E_PLAYWRIGHT_ONION_PROBE_DELAY | int }}
{{ TEST_E2E_PLAYWRIGHT_ONION_PROBE_MAX_TIME | int }}
{{ lookup('container', 'svc-net-tor', 'directories.instance') | quote }}
{{ (TEST_E2E_PLAYWRIGHT_TOR_LOCK if (_playwright_dispatch | bool) else '') | quote }}
register: _playwright_probe
changed_when: false
failed_when: _playwright_probe.rc != 0 and not (_playwright_dispatch | bool)
become: true
when: TEST_E2E_PLAYWRIGHT_IS_ONION | bool
- name: ๐ Run the Playwright container in this play and report it
when: not (_playwright_dispatch | bool)
block:
- name: โถ๏ธ Run Playwright container
ansible.builtin.command:
argv: &playwright_argv >-
{{
['container', 'run', '--rm', '--ipc=host', '--shm-size=1g']
+ (
['--network', 'host']
if IS_SWARM_MODE
else (
['--add-host=host.docker.internal:host-gateway']
+ (
['--add-host=' ~ lookup('tls', 'web-app-keycloak', 'domain') ~ ':host-gateway']
if 'web-app-keycloak' in group_names
else []
)
)
)
+ ['--env-file', _playwright_stage_dir ~ '/.env']
+ ['-e', 'INFINITO_PLAYWRIGHT_KEEP=' ~ test_e2e_playwright_keep]
+ ['-e', 'INFINITO_PLAYWRIGHT_GLOBAL_TIMEOUT_MS=' ~ TEST_E2E_PLAYWRIGHT_GLOBAL_TIMEOUT_MS]
+ ['-e', 'PLAYWRIGHT_PROXY=' ~ TEST_E2E_PLAYWRIGHT_PROXY]
+ ['-e', 'PLAYWRIGHT_WORKERS=' ~ test_e2e_playwright_workers]
+ ['-e', 'PLAYWRIGHT_FULLY_PARALLEL=' ~ TEST_E2E_PLAYWRIGHT_FULLY_PARALLEL]
+ ['-e', 'PLAYWRIGHT_TIMEOUT_FACTOR=' ~ TIMEOUT_FACTOR]
+ (['-e', 'PLAYWRIGHT_ONION_TIMEOUT_MULTIPLIER=' ~ TEST_E2E_PLAYWRIGHT_ONION_TIMEOUT_MULTIPLIER] if (TEST_E2E_PLAYWRIGHT_IS_ONION | bool) else [])
+ ['-v', _playwright_stage_dir ~ ':/e2e']
+ ['-v', _playwright_stage_dir ~ '/volume:/volume']
+ ['-v', _playwright_pass_reports_dir ~ ':/reports']
+ ['-v', TEST_E2E_PLAYWRIGHT_SHARED_NODE_MODULES ~ ':/e2e/node_modules:ro']
+ ['-v', TEST_E2E_PLAYWRIGHT_SHARED_PERSONAS ~ ':/e2e/tests/personas:ro']
+ ['-w', '/e2e']
+ [TEST_E2E_PLAYWRIGHT_IMAGE]
+ ['/bin/bash', '-lc', TEST_E2E_PLAYWRIGHT_COMMAND]
}}
timeout: "{{ lookup('timeout', TEST_E2E_PLAYWRIGHT_RUN_TIMEOUT_EFFECTIVE) | int }}"
environment: &playwright_environment >-
{{
{
'CA_TRUST_CERT_HOST': _playwright_ca_cert_host,
'CA_TRUST_WRAPPER_HOST': _playwright_ca_wrapper_host,
'CA_TRUST_NAME': _playwright_ca_trust_name
} if (_playwright_ca_required | bool) else {}
}}
register: _playwright_run
changed_when: false
failed_when: false
- name: ๐ Report and archive the Playwright run
ansible.builtin.include_tasks: 04_collect.yml
- name: ๐ฌ Queue the Playwright run for the parallel scheduler
when: _playwright_dispatch | bool
block:
- name: ๐ฎ Hand the Playwright run to the parallel scheduler
ansible.builtin.copy:
dest: "{{ TEST_E2E_PLAYWRIGHT_PARALLEL_JOBS_DIR }}/{{ TEST_E2E_PLAYWRIGHT_APPS.index(application_id) }}-{{ application_id }}.job.json"
mode: "0644"
content: >-
{{
{
'id': application_id,
'argv': _playwright_argv[:2] + _playwright_parallel_flags + _playwright_argv[2:],
'env': _playwright_environment,
'timeout': lookup('timeout', TEST_E2E_PLAYWRIGHT_RUN_TIMEOUT_EFFECTIVE) | int,
'after': ([TEST_E2E_PLAYWRIGHT_APPS | first] + lookup('role_tests', application_id, 'playwright.run_after', default=[]))
| intersect(TEST_E2E_PLAYWRIGHT_APPS) | difference([application_id]),
'tor': TEST_E2E_PLAYWRIGHT_IS_ONION | bool,
'workers': _playwright_workers | int,
'memory_mb': lookup('playwright_parallel', 'memory_mb', workers=_playwright_workers) | int,
'stop': ['container', 'rm', '--force', TEST_E2E_PLAYWRIGHT_PARALLEL_CONTAINER_PREFIX ~ application_id],
'meta': {'reports_dir': _playwright_pass_reports_dir}
} | to_json
}}
vars:
_playwright_argv: *playwright_argv
_playwright_environment: *playwright_environment
_playwright_workers: "{{ (_playwright_argv | select('match', '^PLAYWRIGHT_WORKERS=') | first).split('=', 1)[1] }}"
_playwright_parallel_flags:
- "--name={{ TEST_E2E_PLAYWRIGHT_PARALLEL_CONTAINER_PREFIX ~ application_id }}"
- "--oom-score-adj={{ TEST_E2E_PLAYWRIGHT_PARALLEL_OOM_SCORE_ADJ }}"
register: _playwright_job_written
when: _playwright_probe is skipped or (_playwright_probe.rc | default(1)) == 0
- name: ๐ Remember the run the scheduler now owns
ansible.builtin.set_fact:
_playwright_submitted: >-
{{ _playwright_submitted
+ [{'id': application_id, 'job': _playwright_job_written.dest | basename}] }}
when: _playwright_job_written is not skipped
- name: ๐ Leave the run to the serial runner, its onion never answered over Tor
ansible.builtin.set_fact:
_playwright_serial_roles: "{{ _playwright_serial_roles + [application_id] }}"
when: _playwright_job_written is skipped