From 7bd50539e2f136397170de02c17bf7a041606ecd Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 7 Jul 2026 22:06:33 +0530 Subject: [PATCH] feat(scripts): playright scripts for demo screen recording --- .gitignore | 1 - scripts/package-lock.json | 800 ++++++++++++++++++++++++++++++++++++++ scripts/package.json | 8 + scripts/record.js | 222 +++++++++++ scripts/utils.js | 265 +++++++++++++ 5 files changed, 1295 insertions(+), 1 deletion(-) create mode 100644 scripts/package-lock.json create mode 100644 scripts/package.json create mode 100644 scripts/record.js create mode 100644 scripts/utils.js diff --git a/.gitignore b/.gitignore index 3802633..dd335f8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ npm-debug.log data settings.json -scripts logs attachments recipients.csv diff --git a/scripts/package-lock.json b/scripts/package-lock.json new file mode 100644 index 0000000..3a02f2a --- /dev/null +++ b/scripts/package-lock.json @@ -0,0 +1,800 @@ +{ + "name": "scripts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "playwright": "^1.61.1", + "ts-node": "^10.9.2", + "tsx": "^4.23.0", + "typescript": "^6.0.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", + "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsx": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.0.tgz", + "integrity": "sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..69832bb --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,8 @@ +{ + "devDependencies": { + "playwright": "^1.61.1", + "ts-node": "^10.9.2", + "tsx": "^4.23.0", + "typescript": "^6.0.3" + } +} diff --git a/scripts/record.js b/scripts/record.js new file mode 100644 index 0000000..6181bc7 --- /dev/null +++ b/scripts/record.js @@ -0,0 +1,222 @@ +const { chromium } = require('playwright'); +const fs = require('fs'); +const { + injectDemoVisuals, + announce, + clickWithFlourish, + typeSlowly, + zoomToElement, + resetZoom, + pause +} = require('./utils'); + +const BASE_URL = 'http://localhost:3000'; +const VIDEO_PATH = 'dist/full_screen.webm'; + +async function main() { + console.log('Launching browser (Headless: false) and starting video recording...'); + const browser = await chromium.launch({ + slowMo: 40, + headless: false, + args: ['--start-maximized'] + }); + + const context = await browser.newContext({ + viewport: null, + recordVideo: { + dir: 'dist/' + } + }); + + const page = await context.newPage(); + const video = page.video(); + + try { + console.log(`Navigating to ${BASE_URL}...`); + await page.goto(BASE_URL); + await injectDemoVisuals(page); + await pause(page, 1500); + + console.log('Navigating to Templates and scrolling to the bottom...'); + await announce(page, 'Exploring the Template Library...'); + const templatesLink = page.locator('a[title="Templates"]'); + await clickWithFlourish(page, templatesLink); + await page.waitForURL('**/templates*'); + await pause(page, 1000); + + await page.evaluate(() => { + const scrollables = Array.from(document.querySelectorAll('.custom-scrollbar, .overflow-y-auto')) + .filter(el => el.scrollHeight > el.clientHeight); + if (scrollables.length > 0) { + scrollables[0].scrollTo({ top: scrollables[0].scrollHeight, behavior: 'smooth' }); + } else { + window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + } + }); + await pause(page, 2000); + + console.log('Navigating to Databases...'); + await announce(page, 'Viewing Saved Databases...'); + const databasesLink = page.locator('a[title="Databases"]'); + await clickWithFlourish(page, databasesLink); + await page.waitForURL('**/databases*'); + await pause(page, 1500); + + console.log('Navigating to Settings to configure SMTP...'); + await announce(page, 'Configuring a new SMTP Sender Account...'); + const settingsLink = page.locator('a[title="Settings"]'); + await clickWithFlourish(page, settingsLink); + await page.waitForURL('**/settings'); + await pause(page, 1000); + + const addAccountBtn = page.getByRole('button', { name: /Add.*Account/i }).first(); + await clickWithFlourish(page, addAccountBtn); + + console.log('Inputting SMTP credentials...'); + await typeSlowly(page, page.locator('input[placeholder="e.g., Marketing Team"]'), 'Demo Marketing'); + await typeSlowly(page, page.locator('input[placeholder="smtp.gmail.com"]'), 'smtp.gmail.com'); + + const portInput = page.locator('input[placeholder="587"]'); + await portInput.fill(''); + await typeSlowly(page, portInput, '587'); + + await typeSlowly(page, page.locator('input[placeholder="you@example.com"]'), 'acm.marketing@gmail.com'); + await typeSlowly(page, page.locator('input[placeholder="Enter password"]'), 'dummy_password_123'); + + const saveAccountBtn = page.getByRole('button', { name: 'Save Account' }); + await clickWithFlourish(page, saveAccountBtn); + await pause(page, 1500); + + console.log('Navigating to Campaigns...'); + await announce(page, 'Checking existing campaigns...'); + const campaignsLink = page.locator('a[title="Campaigns"]'); + await clickWithFlourish(page, campaignsLink); + await page.waitForURL('**/campaigns*'); + await pause(page, 1500); + + console.log('Navigating to Templates to use a pre-built template...'); + await announce(page, 'Using a pre-built template for our new campaign...'); + await clickWithFlourish(page, templatesLink); + await page.waitForURL('**/templates*'); + + const targetCard = page.locator('.bg-surface-card').filter({ has: page.getByRole('heading', { name: 'Recruitment - Not Selected' }) }).first(); + await targetCard.scrollIntoViewIfNeeded(); + await targetCard.hover(); + await pause(page, 500); + + const useThisBtn = targetCard.getByRole('button', { name: 'Use this' }); + console.log('Zooming in on the "Use this" button...'); + await zoomToElement(page, useThisBtn, 1.4); + await clickWithFlourish(page, useThisBtn); + await pause(page, 1000); + await resetZoom(page); + + console.log('Creating a new campaign from the template...'); + await page.waitForURL('**/campaigns/new?templateId=*'); + await announce(page, 'Creating a new campaign from the template...'); + const campaignNameInput = page.locator('#campaignName'); + await typeSlowly(page, campaignNameInput, 'Fall Recruitment Updates'); + + const createCampaignBtn = page.getByRole('button', { name: 'Create Campaign' }); + await createCampaignBtn.scrollIntoViewIfNeeded(); + + await page.evaluate(() => { + const scrollables = Array.from(document.querySelectorAll('.custom-scrollbar, .overflow-y-auto')) + .filter(el => el.scrollHeight > el.clientHeight); + if (scrollables.length > 0) { + scrollables[0].scrollTo({ top: scrollables[0].scrollHeight, behavior: 'smooth' }); + } else { + window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); + } + }); + await pause(page, 1000); + + await clickWithFlourish(page, createCampaignBtn); + + await page.waitForURL('**/campaigns/*'); + await pause(page, 1500); + + console.log('Toggling the recipients panel...'); + await announce(page, 'Toggling the recipients panel...'); + const recipientsBtn = page.getByRole('button', { name: /Recipients/ }); + await clickWithFlourish(page, recipientsBtn); + await pause(page, 1000); + await clickWithFlourish(page, recipientsBtn); + await pause(page, 1000); + + console.log('Checking the live logs for preflight status...'); + await announce(page, 'Checking the live logs for preflight status...'); + const liveLogsHeader = page.locator('h3', { hasText: 'Live Logs' }); + await clickWithFlourish(page, liveLogsHeader); + await pause(page, 2000); + + console.log('Closing the live logs...'); + await clickWithFlourish(page, liveLogsHeader); + await pause(page, 1000); + + console.log('Importing recipients from a saved database...'); + await announce(page, 'Importing recipients from our saved database...'); + const moreOptionsBtn = page.getByTitle('More Options'); + await clickWithFlourish(page, moreOptionsBtn); + const importDbBtn = page.getByText('Import from Database'); + await clickWithFlourish(page, importDbBtn); + + const dbSelect = page.locator('select'); + await dbSelect.waitFor({ state: 'visible' }); + const dbOptionValue = await page.locator('select option', { hasText: 'AISSMS IOIT ACM Student Chapter Contacts' }).getAttribute('value'); + await dbSelect.selectOption(dbOptionValue); + await pause(page, 500); + + const importRecordsBtn = page.getByRole('button', { name: 'Import Records' }); + await clickWithFlourish(page, importRecordsBtn); + await pause(page, 1500); + + console.log('Opening recipients and previewing emails...'); + await clickWithFlourish(page, recipientsBtn); + await pause(page, 1000); + + await announce(page, 'Previewing the personalized email...'); + const previewBtn = page.locator('table tbody tr').first().locator('button[aria-label="Preview Email"]'); + await clickWithFlourish(page, previewBtn); + await pause(page, 2000); + + console.log('Scrolling through recipient previews...'); + await announce(page, 'Scrolling through recipient previews...'); + const nextBtn = page.locator('button[aria-label="Next Recipient"]'); + await clickWithFlourish(page, nextBtn); + await pause(page, 1500); + await clickWithFlourish(page, nextBtn); + await pause(page, 2000); + + console.log('Closing preview modal...'); + const closeModalBtn = page.getByLabel('Close modal'); + await clickWithFlourish(page, closeModalBtn); + await pause(page, 1000); + + console.log('Opening the confirmation modal...'); + await announce(page, 'Ready to dispatch! Reviewing the final summary...', 3000); + const startSendingBtn = page.getByRole('button', { name: 'Start Sending' }); + await clickWithFlourish(page, startSendingBtn); + await pause(page, 4500); + + console.log('Demo complete! Closing browser and saving video to ' + VIDEO_PATH); + } catch (error) { + console.error('An error occurred during the demo:', error); + throw error; + } finally { + await context.close(); + await browser.close(); + + if (video) { + const tempVideoPath = await video.path(); + if (fs.existsSync(tempVideoPath)) { + fs.renameSync(tempVideoPath, VIDEO_PATH); + } + } + } +} + +main().catch((err) => { + console.error('Fatal error:', err); + process.exit(1); +}); diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 0000000..8d7ab2e --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,265 @@ +async function injectDemoVisuals(page) { + await page.addStyleTag({ + content: ` + #__demo_cursor { + position: fixed; + width: 18px; + height: 18px; + border-radius: 50%; + background: rgba(255, 59, 48, 0.55); + border: 2px solid rgba(255, 59, 48, 0.9); + z-index: 2147483647; + pointer-events: none; + transform: translate(-50%, -50%); + transition: left 0.35s cubic-bezier(0.22, 1, 0.36, 1), top 0.35s cubic-bezier(0.22, 1, 0.36, 1); + } + + #__demo_zoom_wrapper { + transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1); + transform-origin: 0 0; + will-change: transform; + } + + #__demo_click_ring { + position: fixed; + width: 36px; + height: 36px; + border-radius: 50%; + border: 2px solid rgba(255, 59, 48, 0.9); + z-index: 2147483646; + pointer-events: none; + transform: translate(-50%, -50%) scale(0); + opacity: 0; + } + + #__demo_click_ring.pulse { + animation: __demo_pulse 0.5s ease-out; + } + + @keyframes __demo_pulse { + 0% { transform: translate(-50%, -50%) scale(0.4); opacity: 0.9; } + 100% { transform: translate(-50%, -50%) scale(1.8); opacity: 0; } + } + + #__demo_caption_box { + position: fixed; + bottom: 80px; + left: 50%; + transform: translateX(-50%) translateY(20px); + background: #ffffff; + color: #000000; + padding: 16px 36px; + border-radius: 8px; + font-family: system-ui, -apple-system, sans-serif; + font-size: 22px; + font-weight: 600; + box-shadow: 0 10px 30px rgba(0,0,0,0.15); + z-index: 2147483647; + pointer-events: none; + opacity: 0; + transition: opacity 0.4s ease, transform 0.4s ease; + text-align: center; + max-width: 80%; + white-space: nowrap; + } + + #__demo_caption_box.visible { + opacity: 1; + transform: translateX(-50%) translateY(0); + } + `, + }); + + await page.evaluate(() => { + const cursor = document.createElement('div'); + cursor.id = '__demo_cursor'; + document.body.appendChild(cursor); + + const ring = document.createElement('div'); + ring.id = '__demo_click_ring'; + document.body.appendChild(ring); + + const caption = document.createElement('div'); + caption.id = '__demo_caption_box'; + document.body.appendChild(caption); + + const wrapper = document.createElement('div'); + wrapper.id = '__demo_zoom_wrapper'; + + while (document.body.firstChild) { + if ( + document.body.firstChild === cursor || + document.body.firstChild === ring || + document.body.firstChild === caption + ) { + break; + } + + wrapper.appendChild(document.body.firstChild); + } + + document.body.insertBefore(wrapper, cursor); + }); +} + +async function announce(page, text, durationMs = 3000) { + await page.evaluate( + ({ msg, duration }) => { + const box = document.getElementById('__demo_caption_box'); + + if (!box) return; + + if (window.__demo_caption_timeout) { + clearTimeout(window.__demo_caption_timeout); + } + + box.innerHTML = ` + + + + ${msg} + `; + + box.classList.add('visible'); + + requestAnimationFrame(() => { + const rect = document.getElementById('__demo_caption_rect'); + + if (!rect) return; + + const length = rect.getTotalLength(); + + rect.style.strokeDasharray = length; + rect.style.strokeDashoffset = length; + rect.style.transition = `stroke-dashoffset ${duration}ms linear`; + + void rect.getBoundingClientRect(); + + rect.style.strokeDashoffset = '0'; + }); + + window.__demo_caption_timeout = setTimeout(() => { + box.classList.remove('visible'); + }, duration); + }, + { msg: text, duration: durationMs } + ); + + await page.waitForTimeout(200); +} + +async function moveCursorTo(page, x, y, steps = 30) { + await page.evaluate( + ({ x, y }) => { + const cursor = document.getElementById('__demo_cursor'); + + if (cursor) { + cursor.style.left = `${x}px`; + cursor.style.top = `${y}px`; + } + }, + { x, y } + ); + + await page.mouse.move(x, y, { steps }); + await page.waitForTimeout(380); +} + +async function clickWithFlourish(page, locator) { + const box = await locator.boundingBox(); + + if (!box) { + throw new Error('Element not visible for clickWithFlourish'); + } + + const x = box.x + box.width / 2; + const y = box.y + box.height / 2; + + await moveCursorTo(page, x, y); + + await page.evaluate( + ({ x, y }) => { + const ring = document.getElementById('__demo_click_ring'); + + if (ring) { + ring.style.left = `${x}px`; + ring.style.top = `${y}px`; + ring.classList.remove('pulse'); + void ring.offsetWidth; + ring.classList.add('pulse'); + } + }, + { x, y } + ); + + await page.waitForTimeout(150); + await locator.click(); +} + +async function typeSlowly(page, locator, text, minDelay = 55, maxDelay = 130) { + const box = await locator.boundingBox(); + + if (box) { + await moveCursorTo(page, box.x + box.width / 2, box.y + box.height / 2); + } + + await locator.click(); + + for (const char of text) { + await locator.pressSequentially(char, { delay: 0 }); + const delay = minDelay + Math.random() * (maxDelay - minDelay); + await page.waitForTimeout(delay); + } +} + +async function zoomToElement(page, locator, scale = 1.6) { + const box = await locator.boundingBox(); + + if (!box) { + throw new Error('Element not visible for zoomToElement'); + } + + const x = box.x + box.width / 2; + const y = box.y + box.height / 2; + + await page.evaluate( + ({ x, y, scale }) => { + const wrapper = document.getElementById('__demo_zoom_wrapper'); + + if (wrapper) { + wrapper.style.transformOrigin = `${x}px ${y}px`; + wrapper.style.transform = `scale(${scale})`; + } + }, + { x, y, scale } + ); + + await page.waitForTimeout(650); +} + +async function resetZoom(page) { + await page.evaluate(() => { + const wrapper = document.getElementById('__demo_zoom_wrapper'); + + if (wrapper) { + wrapper.style.transform = 'scale(1)'; + } + }); + + await page.waitForTimeout(650); +} + +async function pause(page, ms = 1200) { + await page.waitForTimeout(ms); +} + +module.exports = { + injectDemoVisuals, + announce, + moveCursorTo, + clickWithFlourish, + typeSlowly, + zoomToElement, + resetZoom, + pause, +};