diff --git a/README.md b/README.md index 4593a08..adeca96 100644 --- a/README.md +++ b/README.md @@ -50,3 +50,11 @@ bun run deploy # build + screenly edge-app deploy --path=dist/ bun run test # hermetic unit tests, no network access bun run test:live # hits TradingView's real embed scripts, run on demand ``` + +## Screenshots + +```bash +bun run screenshots +``` + +This generates WebP screenshots for all supported resolutions into the `screenshots/` directory. Advanced Chart is captured at every resolution; the other widget types are captured at 1080p and 4K, landscape and portrait. diff --git a/bun.lock b/bun.lock index f51720d..4c0b136 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "": { "name": "tradingview-widget", "devDependencies": { + "@playwright/test": "^1.58.0", "@screenly/edge-apps": "^1.0.0", "@types/bun": "^1.3.14", "bun-types": "^1.3.14", @@ -378,6 +379,8 @@ "@photostructure/tz-lookup": ["@photostructure/tz-lookup@11.6.0", "", {}, "sha512-AHXaBp/bY9nd2pyGi7ldtzXYEZ3ARf7p4EG0XsizmmCxLYMbq+yITj7Z1etO29BiA3YW7aqrcrfToDcBD0grbA=="], + "@playwright/test": ["@playwright/test@1.62.1", "", { "dependencies": { "playwright": "1.62.1" }, "bin": { "playwright": "cli.js" } }, "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.5", "", { "os": "android", "cpu": "arm64" }, "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ=="], "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw=="], @@ -658,7 +661,7 @@ "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], "fuzzball": ["fuzzball@2.2.6", "", { "dependencies": { "heap": ">=0.2.0", "lodash": "^4.17.21", "setimmediate": "^1.0.5" } }, "sha512-RFYKY0GGDOmocz6rYmvOk9qOnV2UCsLHbVPl5zfZVvcdBlilDg/84WSgAvOCGDq+UEnpnqjZMHk6a/hamC5VuQ=="], @@ -812,6 +815,10 @@ "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + "playwright": ["playwright@1.62.1", "", { "dependencies": { "playwright-core": "1.62.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg=="], + + "playwright-core": ["playwright-core@1.62.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw=="], + "postcss": ["postcss@8.5.19", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ=="], "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], @@ -972,6 +979,8 @@ "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -985,5 +994,7 @@ "msgpackr-extract/node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="], "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], } } diff --git a/e2e/screenshots.spec.ts b/e2e/screenshots.spec.ts new file mode 100644 index 0000000..c5f777a --- /dev/null +++ b/e2e/screenshots.spec.ts @@ -0,0 +1,141 @@ +import { test, type Browser } from '@playwright/test' +import { + createMockScreenlyForScreenshots, + FIXED_SCREENSHOT_DATE, + getScreenshotsDir, + RESOLUTIONS, + setupClockMock, + setupScreenlyJsMock, +} from '@screenly/edge-apps/test/screenshots' +import path from 'path' + +const BASE_SETTINGS = { + symbol: 'NASDAQ:AAPL', + theme: 'dark', + interval: 'D', + chart_style: '1', + allow_symbol_change: 'false', + hide_top_toolbar: 'false', + hide_side_toolbar: 'true', + hide_legend: 'false', + locale: 'en', + heatmap_dataset: 'SPX500', + heatmap_grouping: 'sector', + display_errors: 'false', +} + +const METADATA = { + coordinates: [37.3861, -122.0839], + location: 'Silicon Valley, USA', +} + +const { screenlyJsContent: advancedChartScreenlyJsContent } = + createMockScreenlyForScreenshots(METADATA, { + ...BASE_SETTINGS, + widget_type: 'advanced_chart', + }) + +const { screenlyJsContent: symbolOverviewScreenlyJsContent } = + createMockScreenlyForScreenshots(METADATA, { + ...BASE_SETTINGS, + widget_type: 'symbol_overview', + }) + +const { screenlyJsContent: stockHeatmapScreenlyJsContent } = + createMockScreenlyForScreenshots(METADATA, { + ...BASE_SETTINGS, + widget_type: 'stock_heatmap', + }) + +const { screenlyJsContent: cryptoHeatmapScreenlyJsContent } = + createMockScreenlyForScreenshots(METADATA, { + ...BASE_SETTINGS, + widget_type: 'crypto_heatmap', + }) + +type BrowserContext = Awaited> + +async function takeScreenshot( + browser: Browser, + width: number, + height: number, + filename: string, + screenlyJsContent: string, + setup: (context: BrowserContext) => Promise +): Promise { + const screenshotsDir = getScreenshotsDir() + const context = await browser.newContext({ viewport: { width, height } }) + const page = await context.newPage() + + await setupClockMock(page, FIXED_SCREENSHOT_DATE) + await setupScreenlyJsMock(page, screenlyJsContent) + await setup(context) + + await page.goto('/?animations=false') + // Slack waits for networkidle; TradingView's embed keeps sockets/polling + // open, so that wait times out. The iframe appearing is the ready signal. + await page.locator('iframe').first().waitFor({ state: 'visible' }) + await page.waitForTimeout(3000) + + await page.screenshot({ + path: path.join(screenshotsDir, filename), + fullPage: false, + }) + + await context.close() +} + +test.describe.configure({ timeout: 60_000 }) + +for (const { width, height } of RESOLUTIONS) { + test(`screenshot advanced chart ${width}x${height}`, async ({ browser }) => { + await takeScreenshot( + browser, + width, + height, + `advanced-chart-${width}x${height}.png`, + advancedChartScreenlyJsContent, + async () => undefined + ) + }) +} + +for (const [width, height] of [ + [1920, 1080], + [1080, 1920], + [3840, 2160], + [2160, 3840], +]) { + test(`screenshot symbol overview ${width}x${height}`, async ({ browser }) => { + await takeScreenshot( + browser, + width, + height, + `symbol-overview-${width}x${height}.png`, + symbolOverviewScreenlyJsContent, + async () => undefined + ) + }) + + test(`screenshot stock heatmap ${width}x${height}`, async ({ browser }) => { + await takeScreenshot( + browser, + width, + height, + `stock-heatmap-${width}x${height}.png`, + stockHeatmapScreenlyJsContent, + async () => undefined + ) + }) + + test(`screenshot crypto heatmap ${width}x${height}`, async ({ browser }) => { + await takeScreenshot( + browser, + width, + height, + `crypto-heatmap-${width}x${height}.png`, + cryptoHeatmapScreenlyJsContent, + async () => undefined + ) + }) +} diff --git a/package.json b/package.json index d53c8f5..337877d 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "dependencies": {}, "prettier": "./.prettierrc.json", "devDependencies": { + "@playwright/test": "^1.58.0", "@screenly/edge-apps": "^1.0.0", "@types/bun": "^1.3.14", "bun-types": "^1.3.14", diff --git a/screenshots/advanced-chart-1080x1920.webp b/screenshots/advanced-chart-1080x1920.webp new file mode 100644 index 0000000..13c3a51 Binary files /dev/null and b/screenshots/advanced-chart-1080x1920.webp differ diff --git a/screenshots/advanced-chart-1280x720.webp b/screenshots/advanced-chart-1280x720.webp new file mode 100644 index 0000000..a284882 Binary files /dev/null and b/screenshots/advanced-chart-1280x720.webp differ diff --git a/screenshots/advanced-chart-1920x1080.webp b/screenshots/advanced-chart-1920x1080.webp new file mode 100644 index 0000000..cded8b3 Binary files /dev/null and b/screenshots/advanced-chart-1920x1080.webp differ diff --git a/screenshots/advanced-chart-2160x3840.webp b/screenshots/advanced-chart-2160x3840.webp new file mode 100644 index 0000000..0dcfd89 Binary files /dev/null and b/screenshots/advanced-chart-2160x3840.webp differ diff --git a/screenshots/advanced-chart-2160x4096.webp b/screenshots/advanced-chart-2160x4096.webp new file mode 100644 index 0000000..8af56aa Binary files /dev/null and b/screenshots/advanced-chart-2160x4096.webp differ diff --git a/screenshots/advanced-chart-3840x2160.webp b/screenshots/advanced-chart-3840x2160.webp new file mode 100644 index 0000000..7a224d9 Binary files /dev/null and b/screenshots/advanced-chart-3840x2160.webp differ diff --git a/screenshots/advanced-chart-4096x2160.webp b/screenshots/advanced-chart-4096x2160.webp new file mode 100644 index 0000000..cd870a7 Binary files /dev/null and b/screenshots/advanced-chart-4096x2160.webp differ diff --git a/screenshots/advanced-chart-480x800.webp b/screenshots/advanced-chart-480x800.webp new file mode 100644 index 0000000..bac30b0 Binary files /dev/null and b/screenshots/advanced-chart-480x800.webp differ diff --git a/screenshots/advanced-chart-720x1280.webp b/screenshots/advanced-chart-720x1280.webp new file mode 100644 index 0000000..dfc4b5f Binary files /dev/null and b/screenshots/advanced-chart-720x1280.webp differ diff --git a/screenshots/advanced-chart-800x480.webp b/screenshots/advanced-chart-800x480.webp new file mode 100644 index 0000000..f54545a Binary files /dev/null and b/screenshots/advanced-chart-800x480.webp differ diff --git a/screenshots/crypto-heatmap-1080x1920.webp b/screenshots/crypto-heatmap-1080x1920.webp new file mode 100644 index 0000000..8c45aa1 Binary files /dev/null and b/screenshots/crypto-heatmap-1080x1920.webp differ diff --git a/screenshots/crypto-heatmap-1920x1080.webp b/screenshots/crypto-heatmap-1920x1080.webp new file mode 100644 index 0000000..ad488bc Binary files /dev/null and b/screenshots/crypto-heatmap-1920x1080.webp differ diff --git a/screenshots/crypto-heatmap-2160x3840.webp b/screenshots/crypto-heatmap-2160x3840.webp new file mode 100644 index 0000000..15adfd6 Binary files /dev/null and b/screenshots/crypto-heatmap-2160x3840.webp differ diff --git a/screenshots/crypto-heatmap-3840x2160.webp b/screenshots/crypto-heatmap-3840x2160.webp new file mode 100644 index 0000000..35d843b Binary files /dev/null and b/screenshots/crypto-heatmap-3840x2160.webp differ diff --git a/screenshots/stock-heatmap-1080x1920.webp b/screenshots/stock-heatmap-1080x1920.webp new file mode 100644 index 0000000..59a9806 Binary files /dev/null and b/screenshots/stock-heatmap-1080x1920.webp differ diff --git a/screenshots/stock-heatmap-1920x1080.webp b/screenshots/stock-heatmap-1920x1080.webp new file mode 100644 index 0000000..902ded5 Binary files /dev/null and b/screenshots/stock-heatmap-1920x1080.webp differ diff --git a/screenshots/stock-heatmap-2160x3840.webp b/screenshots/stock-heatmap-2160x3840.webp new file mode 100644 index 0000000..6cc06b2 Binary files /dev/null and b/screenshots/stock-heatmap-2160x3840.webp differ diff --git a/screenshots/stock-heatmap-3840x2160.webp b/screenshots/stock-heatmap-3840x2160.webp new file mode 100644 index 0000000..5c97d58 Binary files /dev/null and b/screenshots/stock-heatmap-3840x2160.webp differ diff --git a/screenshots/symbol-overview-1080x1920.webp b/screenshots/symbol-overview-1080x1920.webp new file mode 100644 index 0000000..e34ca7a Binary files /dev/null and b/screenshots/symbol-overview-1080x1920.webp differ diff --git a/screenshots/symbol-overview-1920x1080.webp b/screenshots/symbol-overview-1920x1080.webp new file mode 100644 index 0000000..1e2aa47 Binary files /dev/null and b/screenshots/symbol-overview-1920x1080.webp differ diff --git a/screenshots/symbol-overview-2160x3840.webp b/screenshots/symbol-overview-2160x3840.webp new file mode 100644 index 0000000..0c4e0a6 Binary files /dev/null and b/screenshots/symbol-overview-2160x3840.webp differ diff --git a/screenshots/symbol-overview-3840x2160.webp b/screenshots/symbol-overview-3840x2160.webp new file mode 100644 index 0000000..5cd3d7b Binary files /dev/null and b/screenshots/symbol-overview-3840x2160.webp differ