diff --git a/.github/scripts/build_npm_applications.sh b/.github/scripts/build_npm_applications.sh new file mode 100644 index 00000000..53bd3025 --- /dev/null +++ b/.github/scripts/build_npm_applications.sh @@ -0,0 +1,46 @@ +PRE_OR_POST_RELEASE=$1 +MODULE_TYPE=$2 + + +if [ "$PRE_OR_POST_RELEASE" = "PRE" ]; then + if [ "$MODULE_TYPE" = "NPM-ES" ]; then + cd smoke/smoke-test-application-NPM-ES + npm uninstall aws-rum-web + npm run clean + npm install + npm install /home/runner/work/aws-rum-web/aws-rum-web #Install locally + npm run build + elif [ "$MODULE_TYPE" = "NPM-CJS" ]; then + cd smoke/smoke-test-application-NPM-CJS + npm uninstall aws-rum-web + npm run clean + npm install + npm install $(npm pack /home/runner/work/aws-rum-web/aws-rum-web | tail -1) #Install locally + npm run build + else + echo "Not a valid module type" + fi +elif [ "$PRE_OR_POST_RELEASE" = "POST" ]; then + + if [ "$MODULE_TYPE" = "NPM-ES" ]; then + cd smoke/smoke-test-application-NPM-ES + npm uninstall aws-rum-web + npm run clean + npm install + npm install aws-rum-web #Install latest released direct from NPM + npm run build + elif [ "$MODULE_TYPE" = "NPM-CJS" ]; then + cd smoke/smoke-test-application-NPM-CJS + npm uninstall aws-rum-web + npm run clean + npm install + npm install aws-rum-web #Install latest released direct from NPM + npm run build + else + echo "Not a valid module type" + fi +else + echo "No valid option. Please provide PRE OR POST" +fi + + diff --git a/.github/scripts/update_smoke_test.sh b/.github/scripts/update_smoke_test.sh index e0725162..ffe083f2 100644 --- a/.github/scripts/update_smoke_test.sh +++ b/.github/scripts/update_smoke_test.sh @@ -1,3 +1,4 @@ +# App monitor details MONITOR_ID=$1 REGION=$2 GUEST_ARN=$3 @@ -6,6 +7,17 @@ ENDPOINT=$5 CDN=$6 VERSION=$(npm pkg get version | sed 's/"//g')/cwr.js CDN+=${VERSION} +INSTALL_METHOD=$7 + +if [ "$INSTALL_METHOD" = "CDN" ]; then awk '{sub(/\$MONITOR_ID/,MONITOR_ID);sub(/\$REGION/,REGION);sub(/\$CDN/,CDN);sub(/\$GUEST_ARN/,GUEST_ARN);sub(/\$IDENTITY_POOL/,IDENTITY_POOL);sub(/\$ENDPOINT/,ENDPOINT);}1' \ - MONITOR_ID="'$MONITOR_ID'" REGION="'$REGION'" CDN="'$CDN'" GUEST_ARN="'$GUEST_ARN'" IDENTITY_POOL="'$IDENTITY_POOL'" ENDPOINT="'$ENDPOINT'" app/smoke.html - \ No newline at end of file + MONITOR_ID="'$MONITOR_ID'" REGION="'$REGION'" CDN="'$CDN'" GUEST_ARN="'$GUEST_ARN'" IDENTITY_POOL="'$IDENTITY_POOL'" ENDPOINT="'$ENDPOINT'" smoke/smoke-test-application-CDN/smoke.html +elif [ "$INSTALL_METHOD" = "NPM_ES" ]; then +awk '{sub(/\$MONITOR_ID/,MONITOR_ID);sub(/\$REGION/,REGION);sub(/\$CDN/,CDN);sub(/\$GUEST_ARN/,GUEST_ARN);sub(/\$IDENTITY_POOL/,IDENTITY_POOL);sub(/\$ENDPOINT/,ENDPOINT);}1' \ + MONITOR_ID="'$MONITOR_ID'" REGION="'$REGION'" CDN="'$CDN'" GUEST_ARN="'$GUEST_ARN'" IDENTITY_POOL="'$IDENTITY_POOL'" ENDPOINT="'$ENDPOINT'" smoke/smoke-test-application-NPM-ES/src/loader-npm-rum.ts + elif [ "$INSTALL_METHOD" = "NPM_CJS" ]; then +awk '{sub(/\$MONITOR_ID/,MONITOR_ID);sub(/\$REGION/,REGION);sub(/\$CDN/,CDN);sub(/\$GUEST_ARN/,GUEST_ARN);sub(/\$IDENTITY_POOL/,IDENTITY_POOL);sub(/\$ENDPOINT/,ENDPOINT);}1' \ + MONITOR_ID="'$MONITOR_ID'" REGION="'$REGION'" CDN="'$CDN'" GUEST_ARN="'$GUEST_ARN'" IDENTITY_POOL="'$IDENTITY_POOL'" ENDPOINT="'$ENDPOINT'" smoke/smoke-test-application-NPM-CJS/src/loader-npm-rum.ts +else + echo "No valid installation method input" +fi diff --git a/.github/scripts/upload_smoke_test.sh b/.github/scripts/upload_smoke_test.sh index af387310..ea3b6a5c 100644 --- a/.github/scripts/upload_smoke_test.sh +++ b/.github/scripts/upload_smoke_test.sh @@ -1,3 +1,13 @@ bucket=$1 -key=smoke-$(npm pkg get version | sed 's/"//g').html -aws s3api put-object --bucket $bucket --key "$key" --body processed_smoke.html --content-type "text/html" +version=$(npm pkg get version | sed 's/"//g') + +# CDN +aws s3api put-object --bucket $bucket --key "smoke-$version.html" --body processed_smoke.html --content-type "text/html" + +# NPM ES +aws s3api put-object --bucket $bucket --key "npm/es/$version/smoke.html" --body smoke/smoke-test-application-NPM-ES/app/smoke.html --content-type "text/html" +aws s3api put-object --bucket $bucket --key "npm/es/$version/loader_npm_rum_tmp.js" --body smoke/smoke-test-application-NPM-ES/build/dev/loader_npm_rum_tmp.js --content-type application/x-javascript + +# NPM CJS +aws s3api put-object --bucket $bucket --key "npm/cjs/$version/smoke.html" --body smoke/smoke-test-application-NPM-CJS/app/smoke.html --content-type "text/html" +aws s3api put-object --bucket $bucket --key "npm/cjs/$version/loader_npm_rum_tmp.js" --body smoke/smoke-test-application-NPM-CJS/build/dev/loader_npm_rum_tmp.js --content-type application/x-javascript diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index d8fdae00..cb78ce18 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -67,13 +67,37 @@ jobs: curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE - - name: Update Smoke Test Application - id: update-smoke-test + - name: Update Smoke Test Application - CDN + id: update-smoke-test-cdn run: | chmod u+x .github/scripts/update_smoke_test.sh - .github/scripts/update_smoke_test.sh ${{ secrets.SMOKE_MONITOR }} ${{ secrets.SMOKE_REGION }} ${{ secrets.SMOKE_ARN }} ${{ secrets.SMOKE_IDENTITY }} ${{ secrets.CONFIG_ENDPOINT }} ${{ secrets.CDN }} >> processed_smoke.html + .github/scripts/update_smoke_test.sh ${{ secrets.SMOKE_MONITOR }} ${{ secrets.SMOKE_REGION }} ${{ secrets.SMOKE_ARN }} ${{ secrets.SMOKE_IDENTITY }} ${{ secrets.CONFIG_ENDPOINT }} ${{ secrets.CDN }} "CDN" >> processed_smoke.html - - name: Upload Smoke Test to CloudFront + - name: Update Smoke Test Application - NPM/ES + id: update-smoke-test-npm-es + run: | + chmod u+x .github/scripts/update_smoke_test.sh + .github/scripts/update_smoke_test.sh ${{ secrets.SMOKE_MONITOR }} ${{ secrets.SMOKE_REGION }} ${{ secrets.SMOKE_ARN }} ${{ secrets.SMOKE_IDENTITY }} ${{ secrets.CONFIG_ENDPOINT }} ${{ secrets.CDN }} "NPM_ES" >> smoke/smoke-test-application-NPM-ES/src/loader-npm-rum-tmp.ts + + - name: Update Smoke Test Application - NPM/CJS + id: update-smoke-test-npm-cjs + run: | + chmod u+x .github/scripts/update_smoke_test.sh + .github/scripts/update_smoke_test.sh ${{ secrets.SMOKE_MONITOR }} ${{ secrets.SMOKE_REGION }} ${{ secrets.SMOKE_ARN }} ${{ secrets.SMOKE_IDENTITY }} ${{ secrets.CONFIG_ENDPOINT }} ${{ secrets.CDN }} "NPM_CJS" >> smoke/smoke-test-application-NPM-CJS/src/loader-npm-rum-tmp.ts + + - name: Build Smoke Test Application - NPM/ES + id: build-npm-es-application-pre-release + run: | + chmod u+x .github/scripts/build_npm_applications.sh + .github/scripts/build_npm_applications.sh "PRE" "NPM-ES" + + - name: Build Smoke Test Application - NPM/CJS + id: build-npm-cjs-application-pre-release + run: | + chmod u+x .github/scripts/build_npm_applications.sh + .github/scripts/build_npm_applications.sh "PRE" "NPM-CJS" + + - name: Upload Smoke Tests to CloudFront id: upload-smoke-test run: | chmod u+x .github/scripts/upload_smoke_test.sh @@ -82,12 +106,57 @@ jobs: - name: Install PlayWright run: npx playwright install --with-deps chromium - - name: Run Smoke Test + - name: Run Smoke Test (NPM ES) + env: + URL: ${{ secrets.SMOKE_URL }} + MONITOR: ${{ secrets.SMOKE_MONITOR }} + ENDPOINT: ${{ secrets.SMOKE_ENDPOINT }} + NAME: ${{ secrets.SMOKE_MONITOR_NAME }} + INSTALL_METHOD: 'NPM-ES' + run: npm run smoke:headless + timeout-minutes: 10 + + - name: Fetch AWS Credentials for Smoke Test + run: | + export AWS_ROLE_ARN=${{ secrets.SMOKE_TEST_ROLE }} + export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds + export AWS_DEFAULT_REGION=us-east-1 + + echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV + echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV + echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV + + curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE + + - name: Run Smoke Test (NPM CJS) + env: + URL: ${{ secrets.SMOKE_URL }} + MONITOR: ${{ secrets.SMOKE_MONITOR }} + ENDPOINT: ${{ secrets.SMOKE_ENDPOINT }} + NAME: ${{ secrets.SMOKE_MONITOR_NAME }} + INSTALL_METHOD: 'NPM-CJS' + run: npm run smoke:headless + timeout-minutes: 10 + + - name: Fetch AWS Credentials for Smoke Test + run: | + export AWS_ROLE_ARN=${{ secrets.SMOKE_TEST_ROLE }} + export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds + export AWS_DEFAULT_REGION=us-east-1 + + echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV + echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV + echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV + + curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE + + - name: Run Smoke Test (CDN) env: URL: ${{ secrets.SMOKE_URL }} MONITOR: ${{ secrets.SMOKE_MONITOR }} ENDPOINT: ${{ secrets.SMOKE_ENDPOINT }} NAME: ${{ secrets.SMOKE_MONITOR_NAME }} + INSTALL_METHOD: 'CDN' run: npm run smoke:headless - name: Publish to NPM @@ -95,6 +164,18 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Build Smoke Test Application - NPM/ES (Post NPM Release) + id: build-npm-es-application-post-release + run: | + chmod u+x .github/scripts/build_npm_applications.sh + .github/scripts/build_npm_applications.sh "POST" "NPM-ES" + + - name: Build Smoke Test Application - NPM/CJS (Post NPM Release) + id: build-npm-cjs-application-post-release + run: | + chmod u+x .github/scripts/build_npm_applications.sh + .github/scripts/build_npm_applications.sh "POST" "NPM-CJS" + - name: Create GitHub Release id: create_release uses: actions/create-release@v1 diff --git a/.prettierignore b/.prettierignore index aafe6a9b..63b692c2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,4 @@ coverage src/events package-lock.json docs/ -.vscode +.vscode \ No newline at end of file diff --git a/package.json b/package.json index 8485910e..31c8d075 100644 --- a/package.json +++ b/package.json @@ -56,8 +56,7 @@ "postinteg:local:nightwatch:firefox": "kill $(lsof -t -i:8080)", "smoke:local:headless": "cross-env URL=$URL MONITOR_ID=$MONITOR ENDPOINT=$ENDPOINT NAME=$NAME VERSION=$npm_package_version npx playwright test --config=playwright.local.config.ts", "smoke:local": "cross-env URL=$URL MONITOR_ID=$MONITOR ENDPOINT=$ENDPOINT NAME=$NAME VERSION=$npm_package_version npx playwright test --config=playwright.local.config.ts --headed", - "smoke": "cross-env URL=$URL MONITOR_ID=$MONITOR ENDPOINT=$ENDPOINT NAME=$NAME VERSION=$npm_package_version npx playwright test --config=playwright.config.ts --headed", - "smoke:headless": "cross-env URL=$URL MONITOR_ID=$MONITOR ENDPOINT=$ENDPOINT NAME=$NAME VERSION=$npm_package_version npx playwright test --config=playwright.config.ts", + "smoke:headless": "cross-env URL=$URL MONITOR_ID=$MONITOR ENDPOINT=$ENDPOINT NAME=$NAME VERSION=$npm_package_version INSTALL_METHOD=$INSTALL_METHOD npx playwright test --config=playwright.config.ts", "prepare": "husky install" }, "devDependencies": { diff --git a/playwright.config.ts b/playwright.config.ts index 8b0be1ae..3a89eb99 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -2,11 +2,14 @@ // @ts-check const { devices } = require('@playwright/test'); -const config = { +var config = { forbidOnly: !!process.env.CI, reporter: 'list', workers: process.env.CI ? 4 : undefined, - testDir: 'src/__smoke-test__', + testDir: + process.env.INSTALL_METHOD === 'CDN' + ? 'src/__smoke-test__' + : 'src/__smoke-test-npm__', retries: process.env.CI ? 2 : 2, timeout: 300000, use: { diff --git a/smoke/smoke-test-application-CDN/smoke.html b/smoke/smoke-test-application-CDN/smoke.html new file mode 100644 index 00000000..ff11b9ba --- /dev/null +++ b/smoke/smoke-test-application-CDN/smoke.html @@ -0,0 +1,271 @@ + + + + + RUM Smoke Test + + + + + + + + + +

This application is used for RUM smoke testing.

+
+ + + + + + + + +
+ + + +
+ + + + + + + +
+ + + +
+ +
+ + diff --git a/smoke/smoke-test-application-NPM-CJS/app/smoke.html b/smoke/smoke-test-application-NPM-CJS/app/smoke.html new file mode 100644 index 00000000..4d99a58a --- /dev/null +++ b/smoke/smoke-test-application-NPM-CJS/app/smoke.html @@ -0,0 +1,185 @@ + + + + + RUM Smoke Test + + + + + + + + + + +

This application is used for RUM smoke testing.

+
+ + + + + + + + +
+ + +
+ + + + + + +
+ + + +
+
+ + diff --git a/smoke/smoke-test-application-NPM-CJS/package.json b/smoke/smoke-test-application-NPM-CJS/package.json new file mode 100644 index 00000000..8f801751 --- /dev/null +++ b/smoke/smoke-test-application-NPM-CJS/package.json @@ -0,0 +1,34 @@ +{ + "name": "smoke-test-application-NPM-ES", + "version": "1.0.0", + "type": "commonjs", + "scripts": { + "build": "webpack --config webpack/webpack.dev.js", + "release": "npm run build", + "server": "webpack-dev-server --config webpack/webpack.dev.js", + "clean": "rm -rf build && rm -rf dist && rm -rf node_modules" + }, + "devDependencies": { + "@babel/core": "7.16.0", + "@babel/plugin-transform-runtime": "^7.16.0", + "@babel/preset-env": "~7.6.3", + "@webpack-cli/serve": "^1.6.0", + "babel-loader": "~8.0.6", + "case-sensitive-paths-webpack-plugin": "^2.1.2", + "copy-webpack-plugin": "^6.3.2", + "ts-loader": "^9.2.6", + "typescript": "^4.4.3", + "webpack": "^5.58.1", + "webpack-dev-middleware": "^5.2.1", + "webpack-dev-server": "^4.3.1" + }, + "dependencies": { + "@babel/runtime": "^7.16.0", + "aws-rum-web": "^1.13.6", + "buffer": "^6.0.3", + "html-webpack-plugin": "^5.5.0" + }, + "browserslist": [ + "defaults" + ] +} diff --git a/smoke/smoke-test-application-NPM-CJS/src/loader-npm-rum.ts b/smoke/smoke-test-application-NPM-CJS/src/loader-npm-rum.ts new file mode 100644 index 00000000..95691f75 --- /dev/null +++ b/smoke/smoke-test-application-NPM-CJS/src/loader-npm-rum.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +const { AwsRum, AwsRumConfig } = require('aws-rum-web'); + +let awsRum; + +try { + const config: AwsRumConfig = { + sessionSampleRate: 1, + guestRoleArn: $GUEST_ARN, + identityPoolId: $IDENTITY_POOL, + endpoint: $ENDPOINT, + telemetries: ['performance', 'errors', 'http', 'interaction'], + allowCookies: true, + enableXRay: true + }; + + const APPLICATION_ID: string = $MONITOR_ID; + const APPLICATION_VERSION: string = '1.0.0'; + const APPLICATION_REGION: string = $REGION; + + awsRum: AwsRum = new AwsRum( + APPLICATION_ID, + APPLICATION_VERSION, + APPLICATION_REGION, + config + ); +} catch (error) { + console.log(error); + throw error; +} diff --git a/smoke/smoke-test-application-NPM-CJS/tsconfig.json b/smoke/smoke-test-application-NPM-CJS/tsconfig.json new file mode 100644 index 00000000..3ad37e38 --- /dev/null +++ b/smoke/smoke-test-application-NPM-CJS/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "declaration": true, + "lib": ["dom", "es2018"], + "module": "esnext", + "moduleResolution": "node", + "strict": false, + "target": "es5" + }, + "include": ["src/**/*"] +} diff --git a/smoke/smoke-test-application-NPM-CJS/tsconfig.webpack.json b/smoke/smoke-test-application-NPM-CJS/tsconfig.webpack.json new file mode 100644 index 00000000..5abb2493 --- /dev/null +++ b/smoke/smoke-test-application-NPM-CJS/tsconfig.webpack.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "es2020", + "outDir": "./dist/webpack" + }, + "extends": "./tsconfig" +} diff --git a/smoke/smoke-test-application-NPM-CJS/webpack/webpack.dev.js b/smoke/smoke-test-application-NPM-CJS/webpack/webpack.dev.js new file mode 100644 index 00000000..3be7d4a0 --- /dev/null +++ b/smoke/smoke-test-application-NPM-CJS/webpack/webpack.dev.js @@ -0,0 +1,76 @@ +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const path = require('path'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const babelLoaderOptions = { + presets: [['@babel/preset-env', {}]], + plugins: [ + [ + '@babel/plugin-transform-runtime', + { + absoluteRuntime: false, + corejs: false, + helpers: true, + regenerator: true + } + ] + ] +}; + +module.exports = { + mode: 'development', + devtool: 'inline-source-map', + target: ['web', 'es5'], + entry: { + loader_npm_rum_tmp: './src/loader-npm-rum-tmp.ts' + }, + resolve: { + extensions: ['.ts', '.js', '.json'], + mainFields: ['main', 'module', 'browser'] + }, + plugins: [ + new CopyWebpackPlugin({ + patterns: [{ from: 'app' }] + }), + new CaseSensitivePathsPlugin() + ], + output: { + path: path.join(__dirname, '../build/dev'), + filename: '[name].js', + publicPath: '' + }, + devServer: { + static: path.join(__dirname, '../build/dev'), + port: 9000, + https: false, + hot: true + }, + module: { + rules: [ + { + test: [/\.js$/], + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: babelLoaderOptions + } + }, + { + test: [/\.ts$/], + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: babelLoaderOptions + }, + { + loader: 'ts-loader', + options: { + configFile: 'tsconfig.webpack.json' + } + } + ] + } + ] + } +}; diff --git a/smoke/smoke-test-application-NPM-ES/app/smoke.html b/smoke/smoke-test-application-NPM-ES/app/smoke.html new file mode 100644 index 00000000..4d99a58a --- /dev/null +++ b/smoke/smoke-test-application-NPM-ES/app/smoke.html @@ -0,0 +1,185 @@ + + + + + RUM Smoke Test + + + + + + + + + + +

This application is used for RUM smoke testing.

+
+ + + + + + + + +
+ + +
+ + + + + + +
+ + + +
+
+ + diff --git a/smoke/smoke-test-application-NPM-ES/package.json b/smoke/smoke-test-application-NPM-ES/package.json new file mode 100644 index 00000000..cb22159a --- /dev/null +++ b/smoke/smoke-test-application-NPM-ES/package.json @@ -0,0 +1,31 @@ +{ + "name": "smoke-test-application-NPM-ES", + "version": "1.0.0", + "scripts": { + "build": "webpack --config webpack/webpack.dev.js", + "release": "npm run build", + "server": "webpack-dev-server --config webpack/webpack.dev.js", + "clean": "rm -rf build && rm -rf dist && rm -rf node_modules" + }, + "devDependencies": { + "@babel/core": "7.16.0", + "@babel/plugin-transform-runtime": "^7.16.0", + "@babel/preset-env": "~7.6.3", + "@webpack-cli/serve": "^1.6.0", + "babel-loader": "~8.0.6", + "case-sensitive-paths-webpack-plugin": "^2.1.2", + "copy-webpack-plugin": "^6.3.2", + "ts-loader": "^9.2.6", + "typescript": "^4.4.3", + "webpack": "^5.58.1", + "webpack-dev-middleware": "^5.2.1", + "webpack-dev-server": "^4.3.1" + }, + "dependencies": { + "@babel/runtime": "^7.16.0", + "aws-rum-web": "file:../.." + }, + "browserslist": [ + "defaults" + ] +} diff --git a/smoke/smoke-test-application-NPM-ES/src/loader-npm-rum.ts b/smoke/smoke-test-application-NPM-ES/src/loader-npm-rum.ts new file mode 100644 index 00000000..7488493d --- /dev/null +++ b/smoke/smoke-test-application-NPM-ES/src/loader-npm-rum.ts @@ -0,0 +1,29 @@ +// @ts-nocheck +import { AwsRum, AwsRumConfig } from 'aws-rum-web'; +let awsRum; + +try { + const config: AwsRumConfig = { + sessionSampleRate: 1, + guestRoleArn: $GUEST_ARN, + identityPoolId: $IDENTITY_POOL, + endpoint: $ENDPOINT, + telemetries: ['performance', 'errors', 'http', 'interaction'], + allowCookies: true, + enableXRay: true + }; + + const APPLICATION_ID: string = $MONITOR_ID; + const APPLICATION_VERSION: string = '1.0.0'; + const APPLICATION_REGION: string = $REGION; + + awsRum: AwsRum = new AwsRum( + APPLICATION_ID, + APPLICATION_VERSION, + APPLICATION_REGION, + config + ); +} catch (error) { + console.log(error); + throw error; +} diff --git a/smoke/smoke-test-application-NPM-ES/tsconfig.json b/smoke/smoke-test-application-NPM-ES/tsconfig.json new file mode 100644 index 00000000..3ad37e38 --- /dev/null +++ b/smoke/smoke-test-application-NPM-ES/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "declaration": true, + "lib": ["dom", "es2018"], + "module": "esnext", + "moduleResolution": "node", + "strict": false, + "target": "es5" + }, + "include": ["src/**/*"] +} diff --git a/smoke/smoke-test-application-NPM-ES/tsconfig.webpack.json b/smoke/smoke-test-application-NPM-ES/tsconfig.webpack.json new file mode 100644 index 00000000..5abb2493 --- /dev/null +++ b/smoke/smoke-test-application-NPM-ES/tsconfig.webpack.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "es2020", + "outDir": "./dist/webpack" + }, + "extends": "./tsconfig" +} diff --git a/smoke/smoke-test-application-NPM-ES/webpack/webpack.dev.js b/smoke/smoke-test-application-NPM-ES/webpack/webpack.dev.js new file mode 100644 index 00000000..ee306471 --- /dev/null +++ b/smoke/smoke-test-application-NPM-ES/webpack/webpack.dev.js @@ -0,0 +1,75 @@ +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const path = require('path'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); + +const babelLoaderOptions = { + presets: [['@babel/preset-env', {}]], + plugins: [ + [ + '@babel/plugin-transform-runtime', + { + absoluteRuntime: false, + corejs: false, + helpers: true, + regenerator: true + } + ] + ] +}; + +module.exports = { + mode: 'development', + devtool: 'inline-source-map', + target: ['web', 'es5'], + entry: { + loader_npm_rum_tmp: './src/loader-npm-rum-tmp.ts' + }, + resolve: { + extensions: ['.ts', '.js', '.json'] + }, + plugins: [ + new CopyWebpackPlugin({ + patterns: [{ from: 'app' }] + }), + new CaseSensitivePathsPlugin() + ], + output: { + path: path.join(__dirname, '../build/dev'), + filename: '[name].js', + publicPath: '' + }, + devServer: { + static: path.join(__dirname, '../build/dev'), + port: 9000, + https: false, + hot: true + }, + module: { + rules: [ + { + test: [/\.js$/], + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: babelLoaderOptions + } + }, + { + test: [/\.ts$/], + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + options: babelLoaderOptions + }, + { + loader: 'ts-loader', + options: { + configFile: 'tsconfig.webpack.json' + } + } + ] + } + ] + } +}; diff --git a/src/__smoke-test-npm__/dataplane-integ.spec.ts b/src/__smoke-test-npm__/dataplane-integ.spec.ts new file mode 100644 index 00000000..2ff2345c --- /dev/null +++ b/src/__smoke-test-npm__/dataplane-integ.spec.ts @@ -0,0 +1,72 @@ +import { test, expect } from '@playwright/test'; +import { + getEventsByType, + getUrl, + isDataPlaneRequest +} from 'test-utils/smoke-test-utils'; +import { + PERFORMANCE_NAVIGATION_EVENT_TYPE, + PERFORMANCE_RESOURCE_EVENT_TYPE, + PAGE_VIEW_EVENT_TYPE, + SESSION_START_EVENT_TYPE +} from '../plugins/utils/constant'; + +// Environment variables set through CLI command +const ENDPOINT = process.env.ENDPOINT; +const MONITOR_ID = process.env.MONITOR; +const TEST_URL = getUrl( + process.env.URL, + process.env.VERSION, + process.env.INSTALL_METHOD +); +const TARGET_URL = ENDPOINT + MONITOR_ID; + +test('when web client calls PutRumEvents then the response code is 200', async ({ + page +}) => { + // Open page + await page.goto(TEST_URL); + + // Test will timeout if no successful dataplane request is found + await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); +}); + +test('when web client calls PutRumEvents then the payload contains all events', async ({ + page +}) => { + // Expected number of events per type + const SESSION_START_COUNT = 1; + const PAGEVIEW_COUNT = 1; + const NAVIGATION_COUNT = 1; + const RESOURCE_EVENT_COUNT = 1; + + // Open page + await page.goto(TEST_URL); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const session = getEventsByType(requestBody, SESSION_START_EVENT_TYPE); + const navigation = getEventsByType( + requestBody, + PERFORMANCE_NAVIGATION_EVENT_TYPE + ); + const pageView = getEventsByType(requestBody, PAGE_VIEW_EVENT_TYPE); + const resource = getEventsByType( + requestBody, + PERFORMANCE_RESOURCE_EVENT_TYPE + ); + + // Verify no events are dropped + expect(session.length).toEqual(SESSION_START_COUNT); + expect(pageView.length).toEqual(PAGEVIEW_COUNT); + expect(navigation.length).toEqual(NAVIGATION_COUNT); + expect(resource.length).toEqual(RESOURCE_EVENT_COUNT); +}); diff --git a/src/__smoke-test-npm__/ingestion-integ.spec.ts b/src/__smoke-test-npm__/ingestion-integ.spec.ts new file mode 100644 index 00000000..42faefb2 --- /dev/null +++ b/src/__smoke-test-npm__/ingestion-integ.spec.ts @@ -0,0 +1,329 @@ +import { test, expect } from '@playwright/test'; +import { RUMClient } from '@aws-sdk/client-rum'; +import { + HTTP_EVENT_TYPE, + JS_ERROR_EVENT_TYPE, + LCP_EVENT_TYPE, + FID_EVENT_TYPE, + CLS_EVENT_TYPE, + PERFORMANCE_NAVIGATION_EVENT_TYPE, + PERFORMANCE_RESOURCE_EVENT_TYPE, + PAGE_VIEW_EVENT_TYPE, + SESSION_START_EVENT_TYPE, + DOM_EVENT_TYPE, + XRAY_TRACE_EVENT_TYPE +} from '../plugins/utils/constant'; +import { + getEventIds, + getEventsByType, + getUrl, + isDataPlaneRequest, + verifyIngestionWithRetry +} from 'test-utils/smoke-test-utils'; + +// Environment variables set through CLI command +const ENDPOINT = process.env.ENDPOINT; +const MONITOR_ID = process.env.MONITOR; +const TEST_URL = getUrl( + process.env.URL, + process.env.VERSION, + process.env.INSTALL_METHOD +); +const MONITOR_NAME = process.env.NAME; +const REGION = ENDPOINT.split('.')[2]; +const TARGET_URL = ENDPOINT + MONITOR_ID; + +// Parse region from endpoint +const rumClient = new RUMClient({ region: REGION }); + +// Run the tests in parallel +test.describe.configure({ mode: 'parallel' }); +test('when session start event is sent then event is ingested', async ({ + page +}) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const session = getEventsByType(requestBody, SESSION_START_EVENT_TYPE); + const eventIds = getEventIds(session); + + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when resource event is sent then event is ingested', async ({ page }) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const resource = getEventsByType( + requestBody, + PERFORMANCE_RESOURCE_EVENT_TYPE + ); + const eventIds = getEventIds(resource); + + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when LCP event is sent then event is ingested', async ({ page }) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const clearButton = page.locator('[id=dummyButton]'); + await clearButton.click(); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const lcp = getEventsByType(requestBody, LCP_EVENT_TYPE); + const eventIds = getEventIds(lcp); + + expect(eventIds.length).not.toEqual(0); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when FID event is sent then event is ingested', async ({ page }) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const clearButton = page.locator('[id=dummyButton]'); + await clearButton.click(); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const fid = getEventsByType(requestBody, FID_EVENT_TYPE); + const eventIds = getEventIds(fid); + + expect(eventIds.length).not.toEqual(0); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when navigation events are sent then events are ingested', async ({ + page +}) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const clearButton = page.locator('[id=pushStateOneToHistory]'); + await clearButton.click(); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const navigation = getEventsByType( + requestBody, + PERFORMANCE_NAVIGATION_EVENT_TYPE + ); + const eventIds = getEventIds(navigation); + + // One initial load, one route change + expect(eventIds.length).toEqual(2); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when page view event is sent then the event is ingested', async ({ + page +}) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const clearButton = page.locator('[id=pushStateOneToHistory]'); + await clearButton.click(); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const pageViews = getEventsByType(requestBody, PAGE_VIEW_EVENT_TYPE); + const eventIds = getEventIds(pageViews); + + // One initial load, one route change + expect(eventIds.length).toEqual(2); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when http events are sent then the events are ingested', async ({ + page +}) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const fetch500 = page.locator('[id=httpStatFetch500]'); + const xhr500 = page.locator('[id=httpStatXhr500]'); + await fetch500.click(); + await xhr500.click(); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const httpEvents = getEventsByType(requestBody, HTTP_EVENT_TYPE); + const eventIds = getEventIds(httpEvents); + + // Expect two http events + expect(eventIds.length).toEqual(2); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when CLS event is sent then the event is ingested', async ({ page }) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const cls = page.locator('[id=dispatchCLS]'); + await cls.click(); + + // CLS is reported only when the page is background or unloaded (visibilitychange) + // Thus, while triggering CLS, Web client will use beacon instead of fetch + // As a result, we need to take a substring of the existing target url + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest( + response, + TARGET_URL.substring(0, TARGET_URL.length - 1) + ) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const clsEvents = getEventsByType(requestBody, CLS_EVENT_TYPE); + const eventIds = getEventIds(clsEvents); + + // Expect one cls event + expect(eventIds.length).toEqual(1); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); + +test('when xray event is sent then the event is ingested', async ({ page }) => { + const timestamp = Date.now() - 30000; + + // Open page + await page.goto(TEST_URL); + const http200 = page.locator('[id=httpStatFetch200]'); + await http200.click(); + + // Test will timeout if no successful dataplane request is found + const response = await page.waitForResponse(async (response) => + isDataPlaneRequest(response, TARGET_URL) + ); + + // Parse payload to verify event count + const requestBody = JSON.parse(response.request().postData()); + + const xrayEvent = getEventsByType(requestBody, XRAY_TRACE_EVENT_TYPE); + const eventIds = getEventIds(xrayEvent); + + // Except one xray event + expect(eventIds.length).toEqual(1); + const isIngestionCompleted = await verifyIngestionWithRetry( + rumClient, + eventIds, + timestamp, + MONITOR_NAME, + 5 + ); + expect(isIngestionCompleted).toEqual(true); +}); diff --git a/src/__smoke-test__/dataplane-integ.spec.ts b/src/__smoke-test__/dataplane-integ.spec.ts index 41e01377..2ff2345c 100644 --- a/src/__smoke-test__/dataplane-integ.spec.ts +++ b/src/__smoke-test__/dataplane-integ.spec.ts @@ -14,7 +14,11 @@ import { // Environment variables set through CLI command const ENDPOINT = process.env.ENDPOINT; const MONITOR_ID = process.env.MONITOR; -const TEST_URL = getUrl(process.env.URL, process.env.VERSION); +const TEST_URL = getUrl( + process.env.URL, + process.env.VERSION, + process.env.INSTALL_METHOD +); const TARGET_URL = ENDPOINT + MONITOR_ID; test('when web client calls PutRumEvents then the response code is 200', async ({ diff --git a/src/__smoke-test__/ingestion-integ.spec.ts b/src/__smoke-test__/ingestion-integ.spec.ts index c6404d3c..c4491bad 100644 --- a/src/__smoke-test__/ingestion-integ.spec.ts +++ b/src/__smoke-test__/ingestion-integ.spec.ts @@ -24,7 +24,11 @@ import { // Environment variables set through CLI command const ENDPOINT = process.env.ENDPOINT; const MONITOR_ID = process.env.MONITOR; -const TEST_URL = getUrl(process.env.URL, process.env.VERSION); +const TEST_URL = getUrl( + process.env.URL, + process.env.VERSION, + process.env.INSTALL_METHOD +); const MONITOR_NAME = process.env.NAME; const REGION = ENDPOINT.split('.')[2]; const TARGET_URL = ENDPOINT + MONITOR_ID; diff --git a/src/test-utils/smoke-test-utils.ts b/src/test-utils/smoke-test-utils.ts index 0e5bc093..088c3062 100644 --- a/src/test-utils/smoke-test-utils.ts +++ b/src/test-utils/smoke-test-utils.ts @@ -46,14 +46,23 @@ export const getEventIds = (events: any[]) => { /** Returns the smoke test URL with the right version */ export const getUrl = ( testUrl: string | URL | undefined, - version: string | undefined + version: string | undefined, + install_method: string | undefined ) => { if (!testUrl) { return 'http://localhost:9000/smoke_local.html'; } const url = new URL(testUrl); if (url.pathname === '/') { - return url + `smoke-${version}.html`; + if (install_method === 'CDN') { + return url + `smoke-${version}.html`; + } else if (install_method === 'NPM-ES') { + return url + `npm/es/${version}/smoke.html`; + } else if (install_method === 'NPM-CJS') { + return url + `npm/cjs/${version}/smoke.html`; + } else { + return url.toString(); + } } else { return url.toString(); }