diff --git a/.eslintignore b/.eslintignore index 4d6880d3..14371e69 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ dist examples +__tests__/e2e/tmp/ diff --git a/.gitignore b/.gitignore index b2ff1fcc..12a443c5 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,7 @@ tmp # Vim temp files *.swp + +__tests__/e2e/tmp/ + +.idea/ diff --git a/__tests__/e2e/.npmrc b/__tests__/e2e/.npmrc new file mode 100644 index 00000000..43c97e71 --- /dev/null +++ b/__tests__/e2e/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/__tests__/e2e/docker-compose.yml b/__tests__/e2e/docker-compose.yml new file mode 100644 index 00000000..0b148e9b --- /dev/null +++ b/__tests__/e2e/docker-compose.yml @@ -0,0 +1,63 @@ +version: '3.7' +services: + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} + container_name: elasticsearch + healthcheck: + test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -vq '\"status\":\"red\"'"] + retries: 10 + interval: 20s + environment: + - node.name=elasticsearch + - cluster.name=es-docker-cluster + - cluster.initial_master_nodes=elasticsearch + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + ulimits: + memlock: + soft: -1 + hard: -1 + ports: + - 127.0.0.1:9200:9200 + networks: + - elastic + kibana: + image: docker.elastic.co/kibana/kibana:${STACK_VERSION} + container_name: kibana + depends_on: + - elasticsearch + - synthetic + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:5601/api/status"] + interval: 30s + retries: 10 + start_period: 30s + ports: + - 127.0.0.1:5601:5601 + environment: + ELASTICSEARCH_URL: http://elasticsearch:9200 + ELASTICSEARCH_HOSTS: http://elasticsearch:9200 + networks: + - elastic + synthetic: + image: docker.elastic.co/experimental/synthetics:${STACK_VERSION}-synthetics + container_name: synthetics + depends_on: + - elasticsearch + volumes: + - ../../examples/docker/heartbeat.docker.yml:/usr/share/heartbeat/heartbeat.yml:ro + - ../../:/opt/elastic-synthetics:rw + security_opt: + - seccomp=../../examples/docker/seccomp_profile.json + environment: + ELASTICSEARCH_URL: http://elasticsearch:9200 + ELASTICSEARCH_HOSTS: http://elasticsearch:9200 + command: + -E output.elasticsearch.hosts=["elasticsearch:9200"] + + networks: + - elastic + +networks: + elastic: + driver: bridge diff --git a/__tests__/e2e/package.json b/__tests__/e2e/package.json new file mode 100644 index 00000000..b7071de3 --- /dev/null +++ b/__tests__/e2e/package.json @@ -0,0 +1,14 @@ +{ + "name": "e2e-synthetics", + "private": true, + "main": "index.js", + "license": "MIT", + "scripts": { + "setup": "bash scripts/setup.sh", + "test": "npm i && npm run setup && bash scripts/test.sh" + }, + "devDependencies": { + "@elastic/synthetics": "file:../../", + "axios": "^0.21.0" + } +} diff --git a/__tests__/e2e/scripts/setup.sh b/__tests__/e2e/scripts/setup.sh new file mode 100755 index 00000000..4c9cc48c --- /dev/null +++ b/__tests__/e2e/scripts/setup.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -e + +# variables + +# ensure Docker is running +docker ps &> /dev/null +if [ $? -ne 0 ]; then + echo "⚠️ Please start Docker" + exit 1 +fi + +# formatting +bold=$(tput bold) +normal=$(tput sgr0) + +# paths +E2E_DIR="./" +TMP_DIR="tmp" + +# +# Create tmp folder +################################################## +echo "" # newline +echo "${bold}Temporary folder${normal}" +echo "Temporary files will be stored in: ${E2E_DIR}${TMP_DIR}" +mkdir -p ${TMP_DIR} + + +echo "" # newline +echo "${bold}Starting elasticsearch , kibana and synthetics docker${normal}" +echo "" # newline + +STACK_VERSION=7.10.0 docker-compose --file docker-compose.yml up --remove-orphans > ${TMP_DIR}/docker-logs.log 2>&1 & + diff --git a/__tests__/e2e/scripts/test.sh b/__tests__/e2e/scripts/test.sh new file mode 100755 index 00000000..8254a384 --- /dev/null +++ b/__tests__/e2e/scripts/test.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -e + + +# formatting +bold=$(tput bold) +normal=$(tput sgr0) + + +# Wait for synthetics docker to start +################################################## +echo "" # newline +echo "${bold}Waiting for synthetics docker to start...${normal}" +until [ "`docker inspect -f {{.State.Running}} synthetics`" == "true" ]; do + sleep 0.1; +done; + +echo "✅ Setup completed successfully. Running e2e tests..." + +# +# run e2e tests journey +################################################## + +npx @elastic/synthetics uptime.journey.ts + + diff --git a/__tests__/e2e/uptime.journey.ts b/__tests__/e2e/uptime.journey.ts new file mode 100644 index 00000000..1beb3804 --- /dev/null +++ b/__tests__/e2e/uptime.journey.ts @@ -0,0 +1,123 @@ +/** + * MIT License + * + * Copyright (c) 2020-present, Elastic NV + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +import { beforeAll, journey, step } from '@elastic/synthetics'; +import axios from 'axios'; + + +beforeAll(async ()=> { + await waitForElasticSearch(); + await waitForSyntheticsData(); + await waitForKibana(); +}) + +journey('E2e test synthetics', async ({ page }) => { + + async function refreshUptimeApp(){ + while(!await page.$('div.euiBasicTable')){ + await page.click('[data-test-subj=superDatePickerApplyTimeButton]'); + await page.waitForTimeout(30*1000); + } + } + + step('Go to kibana uptime app', async () => { + await page.goto('http://localhost:5601/app/uptime'); + }); + + step('Check if there is table data', async () => { + await page.click('[data-test-subj=uptimeOverviewPage]'); + await refreshUptimeApp(); + await page.click('div.euiBasicTable', { timeout: 60 * 1000 }); + }); + + step('Click on my monitor', async () => { + await page.click('[data-test-subj=monitor-page-link-my-monitor]'); + }); + + step('It navigates to details page', async () => { + await page.click('[data-test-subj=uptimeMonitorPage]'); + }); +}); + + + +async function waitForSyntheticsData(){ + console.log('Waiting for Synthetics to send data to ES for test monitor'); + let status = false; + + while (!status){ + try { + const { data } = await axios.post('http://localhost:9200/heartbeat-*/_search',{ + "query": { + "bool": { + "filter": [ + { + "term": { + "monitor.id": "my-monitor" + } + }, + { + "exists": { + "field": "summary" + } + } + ] + } + } + }); + + // we want some data in uptime app + status = data?.hits.total.value >= 2; + } + catch (e) {} + } +} + +async function waitForElasticSearch(){ + console.log('Waiting for Elastic Search to start'); + let esStatus = false; + + while (!esStatus){ + try { + const { data } = await axios.get('http://localhost:9200/_cluster/health'); + esStatus = data?.status !=='red'; + } + catch (e) {} + } +} + +async function waitForKibana(){ + console.log('Waiting for kibana server to start'); + + let esStatus = false; + + while (!esStatus){ + try { + const { data } = await axios.get('http://localhost:5601/api/status'); + esStatus = data?.status.overall.state ==='green'; + } + catch (e) {} + } +} diff --git a/jest.config.js b/jest.config.js index 36e4f382..b0356e57 100644 --- a/jest.config.js +++ b/jest.config.js @@ -30,4 +30,5 @@ module.exports = { coverageDirectory: 'coverage', coverageProvider: 'v8', testPathIgnorePatterns: ['dist'], + modulePathIgnorePatterns: ['/e2e/'] };