diff --git a/package-lock.json b/package-lock.json index 1c487b0..a1aa911 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,10 +16,13 @@ "vscode-opentelemetry-viewer": "file:" }, "devDependencies": { + "@types/glob": "^8.1.0", + "@types/jsdom": "^21.1.7", "@types/mocha": "^10.0.10", "@types/node": "^20.17.30", "@types/react": "^19.1.1", "@types/react-dom": "^19.1.2", + "@types/sinon": "^17.0.4", "@types/vscode": "^1.85.0", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", @@ -28,6 +31,8 @@ "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^3.3.2", "eslint": "^9.23.0", + "jsdom": "^26.1.0", + "sinon": "^21.0.0", "ts-loader": "^9.5.2", "typescript": "^5.8.3", "vite": "^6.2.6" @@ -49,6 +54,20 @@ "node": ">=6.0.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", @@ -505,6 +524,121 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", @@ -1528,6 +1662,47 @@ "win32" ] }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1597,18 +1772,48 @@ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", "dev": true }, + "node_modules/@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "^5.1.2", + "@types/node": "*" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mocha": { "version": "10.0.10", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", @@ -1642,6 +1847,30 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/sinon": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.4.tgz", + "integrity": "sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz", + "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.99.1", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.99.1.tgz", @@ -3210,12 +3439,40 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "dev": true }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -3245,6 +3502,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -4266,6 +4530,19 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4519,6 +4796,13 @@ "node": ">=8" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -4669,6 +4953,46 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -5417,6 +5741,13 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nwsapi": { + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6114,6 +6445,13 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -6167,6 +6505,19 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -6408,6 +6759,47 @@ "simple-concat": "^1.0.0" } }, + "node_modules/sinon": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.5", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -6598,6 +6990,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -6763,6 +7162,26 @@ "node": "*" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -6784,6 +7203,32 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -6856,6 +7301,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/typed-rest-client": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", @@ -7056,6 +7511,19 @@ "resolved": "", "link": true }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -7070,6 +7538,16 @@ "node": ">=10.13.0" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/webpack": { "version": "5.99.5", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", @@ -7172,6 +7650,20 @@ "node": ">=18" } }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7296,6 +7788,38 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/xml2js": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", @@ -7318,6 +7842,13 @@ "node": ">=4.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 12f5361..0ddca26 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,21 @@ "group": "navigation" } ] + }, + "configuration": { + "title": "OpenTelemetry Viewer", + "properties": { + "opentelemetryViewer.theme": { + "type": "string", + "enum": [ + "auto", + "light", + "dark" + ], + "default": "auto", + "description": "Theme for the OpenTelemetry viewer. 'auto' follows VS Code's theme, 'light' forces light theme, 'dark' forces dark theme." + } + } } }, "scripts": { @@ -69,13 +84,17 @@ "watch-tests": "tsc -p ./ -w", "pretest": "npm run compile-tests && npm run lint", "lint": "eslint src", - "test": "node ./out/test/runTest.js" + "test": "node ./dist/test/runTest.js", + "test:unit": "npm run compile-tests && mocha --ui tdd --timeout 10000 --colors dist/test/suite/*.test.js" }, "devDependencies": { + "@types/glob": "^8.1.0", + "@types/jsdom": "^21.1.7", "@types/mocha": "^10.0.10", "@types/node": "^20.17.30", "@types/react": "^19.1.1", "@types/react-dom": "^19.1.2", + "@types/sinon": "^17.0.4", "@types/vscode": "^1.85.0", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", @@ -84,6 +103,8 @@ "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^3.3.2", "eslint": "^9.23.0", + "jsdom": "^26.1.0", + "sinon": "^21.0.0", "ts-loader": "^9.5.2", "typescript": "^5.8.3", "vite": "^6.2.6" @@ -95,4 +116,4 @@ "react-dom": "^19.1.0", "vscode-opentelemetry-viewer": "file:" } -} \ No newline at end of file +} diff --git a/src/extension.ts b/src/extension.ts index b09d534..c59a961 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -84,6 +84,23 @@ export function activate(context: vscode.ExtensionContext) { ) : null; + // Get theme configuration + const config = vscode.workspace.getConfiguration('opentelemetryViewer'); + const themeConfig = config.get('theme', 'auto'); + + // Determine the actual theme to use + let isDarkTheme = false; + if (themeConfig === 'dark') { + isDarkTheme = true; + } else if (themeConfig === 'light') { + isDarkTheme = false; + } else { + // Auto mode - detect VS Code theme + const currentTheme = vscode.window.activeColorTheme; + isDarkTheme = currentTheme.kind === vscode.ColorThemeKind.Dark || + currentTheme.kind === vscode.ColorThemeKind.HighContrast; + } + const nonce = getNonce(); panel.webview.html = ` @@ -112,11 +129,15 @@ export function activate(context: vscode.ExtensionContext) { `; - // Send logs to the webview after it's ready + // Send logs and theme to the webview after it's ready panel.webview.onDidReceiveMessage((msg) => { if (msg.type === "ready") { - console.log("[EXT] Webview is ready. Sending logs..."); - panel.webview.postMessage({ type: "loadLogs", payload: logData }); + console.log("[EXT] Webview is ready. Sending logs and theme..."); + panel.webview.postMessage({ + type: "loadLogs", + payload: logData, + theme: isDarkTheme ? 'dark' : 'light' + }); } }); } diff --git a/src/test/README.md b/src/test/README.md new file mode 100644 index 0000000..adbe404 --- /dev/null +++ b/src/test/README.md @@ -0,0 +1,60 @@ +# Tests + +This directory contains the test suite for the OpenTelemetry Viewer extension. + +## Structure + +- `suite/` - Contains the actual test files + - `extension.test.ts` - Tests for the main extension logic (theme detection, configuration) + - `webview.test.ts` - Tests for the webview React component theme handling + - `index.ts` - Test runner configuration +- `fixtures/` - Contains test data files + - `test-logs.jsonl` - Sample log file for testing + - `test-extension.md` - Manual testing instructions +- `runTest.ts` - Main test runner entry point +- `mocha.opts` - Mocha configuration + +## Running Tests + +### All Tests (Integration + Unit) +```bash +npm test +``` + +### Unit Tests Only +```bash +npm run test:unit +``` + +### Manual Testing + +**Note: Make sure you've disabled the public plugin otherwise there will be a conflict.** + +1. Open VS Code +2. Go to Debug panel (Cmd+Shift+D) +3. Select "Run Extension" +4. Press F5 to launch Extension Development Host +5. Open `src/test/fixtures/test-logs.jsonl` +6. Click "OpenTelemetry Viewer" button +7. Test theme settings in VS Code settings + +## Test Coverage + +### Extension Tests (`extension.test.ts`) +- Theme configuration default values +- Theme configuration validation +- VS Code theme detection (light/dark/high contrast) +- Theme determination logic + +### Webview Tests (`webview.test.ts`) +- Container styling for light/dark modes +- Controls styling for light/dark modes +- Row styling for error/warning logs in both themes +- Theme message handling + +## Adding New Tests + +1. Create new `.test.ts` files in the `suite/` directory +2. Use the `suite()` and `test()` functions from Mocha +3. Import `assert` for assertions +4. Use `sinon` for mocking VS Code APIs when needed diff --git a/src/test/fixtures/test-extension.md b/src/test/fixtures/test-extension.md new file mode 100644 index 0000000..5d7b520 --- /dev/null +++ b/src/test/fixtures/test-extension.md @@ -0,0 +1,37 @@ +# Testing Dark Mode Extension + +## Steps to test + +1. **Build the extension**: `npm run build` ✅ +2. **Open VS Code Extension Development Host**: `code --extensionDevelopmentPath=. --new-window` ✅ +3. **Open the test log file**: Open `test-logs.jsonl` in the extension development window +4. **Test the OpenTelemetry Viewer**: Click the "OpenTelemetry Viewer" button in the editor toolbar +5. **Test theme settings**: + - Go to VS Code Settings (Cmd+,) + - Search for "opentelemetryViewer.theme" + - Try different values: "auto", "light", "dark" +6. **Test VS Code theme integration**: + - Change VS Code theme to dark (View > Appearance > Theme > Dark Modern) + - Change VS Code theme to light (View > Appearance > Theme > Light Modern) + - Verify the extension follows VS Code theme when set to "auto" + +## Expected behavior + +- **Light theme**: White background, dark text, light ag-grid theme +- **Dark theme**: Dark background, light text, dark ag-grid theme +- **Auto mode**: Should follow VS Code's current theme +- **Error rows**: Light red background in light mode, dark red in dark mode +- **Warning rows**: Light yellow background in light mode, dark yellow in dark mode + +## Configuration added + +```json +{ + "opentelemetryViewer.theme": { + "type": "string", + "enum": ["auto", "light", "dark"], + "default": "auto", + "description": "Theme for the OpenTelemetry viewer. 'auto' follows VS Code's theme, 'light' forces light theme, 'dark' forces dark theme." + } +} +``` diff --git a/src/test/fixtures/test-logs.jsonl b/src/test/fixtures/test-logs.jsonl new file mode 100644 index 0000000..311b6c4 --- /dev/null +++ b/src/test/fixtures/test-logs.jsonl @@ -0,0 +1,6 @@ +{"timestamp": "2024-01-15T10:30:00Z", "level": "info", "message": "Application started successfully", "service": "web-server", "trace_id": "abc123"} +{"timestamp": "2024-01-15T10:30:05Z", "level": "debug", "message": "Processing user request", "service": "web-server", "user_id": "user123", "trace_id": "def456"} +{"timestamp": "2024-01-15T10:30:10Z", "level": "warn", "message": "High memory usage detected", "service": "web-server", "memory_usage": "85%", "trace_id": "ghi789"} +{"timestamp": "2024-01-15T10:30:15Z", "level": "error", "message": "Database connection failed", "service": "database", "error": "Connection timeout", "trace_id": "jkl012"} +{"timestamp": "2024-01-15T10:30:20Z", "level": "info", "message": "Request completed", "service": "web-server", "duration": "150ms", "status": "200", "trace_id": "def456"} +{"timestamp": "2024-01-15T10:30:25Z", "level": "critical", "message": "System overload detected", "service": "load-balancer", "cpu_usage": "95%", "trace_id": "mno345"} diff --git a/src/test/mocha.opts b/src/test/mocha.opts new file mode 100644 index 0000000..2d1c583 --- /dev/null +++ b/src/test/mocha.opts @@ -0,0 +1,4 @@ +--ui tdd +--timeout 10000 +--colors +--recursive diff --git a/src/test/runTest.ts b/src/test/runTest.ts new file mode 100644 index 0000000..75cb073 --- /dev/null +++ b/src/test/runTest.ts @@ -0,0 +1,22 @@ +import * as path from 'path'; +import { runTests } from '@vscode/test-electron'; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './suite/index'); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error('Failed to run tests', err); + process.exit(1); + } +} + +main(); diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts new file mode 100644 index 0000000..c53be63 --- /dev/null +++ b/src/test/suite/extension.test.ts @@ -0,0 +1,115 @@ +import * as assert from 'assert'; + +// Unit tests for theme logic (without VS Code dependencies) +suite('Extension Theme Logic Tests', () => { + + test('Should determine theme correctly based on config - dark mode', () => { + // Test the theme determination logic from extension.ts + const themeConfig: string = 'dark'; + let isDarkTheme = false; + + if (themeConfig === 'dark') { + isDarkTheme = true; + } else if (themeConfig === 'light') { + isDarkTheme = false; + } + // Note: 'auto' mode would require VS Code API which we test separately + + assert.strictEqual(isDarkTheme, true); + }); + + test('Should determine theme correctly based on config - light mode', () => { + // Test the theme determination logic from extension.ts + const themeConfig: string = 'light'; + let isDarkTheme = true; // Start with opposite to ensure logic works + + if (themeConfig === 'dark') { + isDarkTheme = true; + } else if (themeConfig === 'light') { + isDarkTheme = false; + } + + assert.strictEqual(isDarkTheme, false); + }); + + test('Should handle auto mode correctly with mock dark theme', () => { + // Mock VS Code theme kind constants + const ColorThemeKind = { + Light: 1, + Dark: 2, + HighContrast: 3 + }; + + const themeConfig: string = 'auto'; + let isDarkTheme = false; + + // Simulate VS Code dark theme + const mockCurrentTheme = { kind: ColorThemeKind.Dark }; + + if (themeConfig === 'dark') { + isDarkTheme = true; + } else if (themeConfig === 'light') { + isDarkTheme = false; + } else { + // Auto mode - detect theme + isDarkTheme = mockCurrentTheme.kind === ColorThemeKind.Dark || + mockCurrentTheme.kind === ColorThemeKind.HighContrast; + } + + assert.strictEqual(isDarkTheme, true); + }); + + test('Should handle auto mode correctly with mock light theme', () => { + // Mock VS Code theme kind constants + const ColorThemeKind = { + Light: 1, + Dark: 2, + HighContrast: 3 + }; + + const themeConfig: string = 'auto'; + let isDarkTheme = true; // Start with opposite + + // Simulate VS Code light theme + const mockCurrentTheme = { kind: ColorThemeKind.Light }; + + if (themeConfig === 'dark') { + isDarkTheme = true; + } else if (themeConfig === 'light') { + isDarkTheme = false; + } else { + // Auto mode - detect theme + isDarkTheme = mockCurrentTheme.kind === ColorThemeKind.Dark || + mockCurrentTheme.kind === ColorThemeKind.HighContrast; + } + + assert.strictEqual(isDarkTheme, false); + }); + + test('Should handle high contrast theme as dark', () => { + // Mock VS Code theme kind constants + const ColorThemeKind = { + Light: 1, + Dark: 2, + HighContrast: 3 + }; + + const themeConfig: string = 'auto'; + let isDarkTheme = false; + + // Simulate VS Code high contrast theme + const mockCurrentTheme = { kind: ColorThemeKind.HighContrast }; + + if (themeConfig === 'dark') { + isDarkTheme = true; + } else if (themeConfig === 'light') { + isDarkTheme = false; + } else { + // Auto mode - detect theme (high contrast should be treated as dark) + isDarkTheme = mockCurrentTheme.kind === ColorThemeKind.Dark || + mockCurrentTheme.kind === ColorThemeKind.HighContrast; + } + + assert.strictEqual(isDarkTheme, true); + }); +}); diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts new file mode 100644 index 0000000..1ce68f6 --- /dev/null +++ b/src/test/suite/index.ts @@ -0,0 +1,36 @@ +import * as path from 'path'; +import Mocha from 'mocha'; +import { glob } from 'glob'; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: 'tdd', + color: true + }); + + const testsRoot = path.resolve(__dirname, '..'); + + return new Promise((c, e) => { + glob('**/**.test.js', { cwd: testsRoot }).then((files: string[]) => { + // Add files to the test suite + files.forEach((f: string) => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run((failures: number) => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }).catch((err: any) => { + e(err); + }); + }); +} diff --git a/src/test/suite/webview.test.ts b/src/test/suite/webview.test.ts new file mode 100644 index 0000000..e793fbb --- /dev/null +++ b/src/test/suite/webview.test.ts @@ -0,0 +1,150 @@ +import * as assert from 'assert'; + +// Unit tests for webview theme logic (without DOM dependencies) +suite('Webview Theme Logic Tests', () => { + + test('Should determine correct theme colors for light mode', () => { + const isDarkTheme = false; + + // Test container styles + const containerStyle = { + height: "100vh", + width: "100%", + backgroundColor: isDarkTheme ? "#1e1e1e" : "#ffffff", + color: isDarkTheme ? "#ffffff" : "#000000" + }; + + assert.strictEqual(containerStyle.backgroundColor, "#ffffff"); + assert.strictEqual(containerStyle.color, "#000000"); + }); + + test('Should determine correct theme colors for dark mode', () => { + const isDarkTheme = true; + + // Test container styles + const containerStyle = { + height: "100vh", + width: "100%", + backgroundColor: isDarkTheme ? "#1e1e1e" : "#ffffff", + color: isDarkTheme ? "#ffffff" : "#000000" + }; + + assert.strictEqual(containerStyle.backgroundColor, "#1e1e1e"); + assert.strictEqual(containerStyle.color, "#ffffff"); + }); + + test('Should determine correct controls styling for light mode', () => { + const isDarkTheme = false; + + const controlsStyle = { + padding: 10, + display: "flex", + flexWrap: "wrap" as const, + gap: 10, + backgroundColor: isDarkTheme ? "#2d2d2d" : "#f5f5f5", + borderBottom: `1px solid ${isDarkTheme ? "#404040" : "#e0e0e0"}` + }; + + assert.strictEqual(controlsStyle.backgroundColor, "#f5f5f5"); + assert.strictEqual(controlsStyle.borderBottom, "1px solid #e0e0e0"); + }); + + test('Should determine correct controls styling for dark mode', () => { + const isDarkTheme = true; + + const controlsStyle = { + padding: 10, + display: "flex", + flexWrap: "wrap" as const, + gap: 10, + backgroundColor: isDarkTheme ? "#2d2d2d" : "#f5f5f5", + borderBottom: `1px solid ${isDarkTheme ? "#404040" : "#e0e0e0"}` + }; + + assert.strictEqual(controlsStyle.backgroundColor, "#2d2d2d"); + assert.strictEqual(controlsStyle.borderBottom, "1px solid #404040"); + }); + + test('Should determine correct row styling for error rows in light mode', () => { + const isDarkTheme = false; + const values = ["error", "critical"]; + + // Simulate the row styling logic + let backgroundColor = undefined; + + if (values.some((v) => ["error", "err", "crit", "critical", "sev", "severe"].includes(v))) { + backgroundColor = isDarkTheme ? "#4a1a1a" : "#ffe5e5"; + } + + assert.strictEqual(backgroundColor, "#ffe5e5"); + }); + + test('Should determine correct row styling for error rows in dark mode', () => { + const isDarkTheme = true; + const values = ["error", "critical"]; + + // Simulate the row styling logic + let backgroundColor = undefined; + + if (values.some((v) => ["error", "err", "crit", "critical", "sev", "severe"].includes(v))) { + backgroundColor = isDarkTheme ? "#4a1a1a" : "#ffe5e5"; + } + + assert.strictEqual(backgroundColor, "#4a1a1a"); + }); + + test('Should determine correct row styling for warning rows in light mode', () => { + const isDarkTheme = false; + const values = ["warn", "warning"]; + + // Simulate the row styling logic + let backgroundColor = undefined; + + if (values.some((v) => ["warn", "warning"].includes(v))) { + backgroundColor = isDarkTheme ? "#4a3a1a" : "#fff8dc"; + } + + assert.strictEqual(backgroundColor, "#fff8dc"); + }); + + test('Should determine correct row styling for warning rows in dark mode', () => { + const isDarkTheme = true; + const values = ["warn", "warning"]; + + // Simulate the row styling logic + let backgroundColor = undefined; + + if (values.some((v) => ["warn", "warning"].includes(v))) { + backgroundColor = isDarkTheme ? "#4a3a1a" : "#fff8dc"; + } + + assert.strictEqual(backgroundColor, "#4a3a1a"); + }); + + test('Should handle theme message correctly', () => { + // Simulate receiving a theme message + const messageData = { + type: "loadLogs", + payload: [ + { timestamp: "2024-01-01", level: "info", message: "test" } + ], + theme: "dark" + }; + + // Simulate theme state update logic + let isDarkTheme = false; + if (messageData.theme) { + isDarkTheme = messageData.theme === 'dark'; + } + + assert.strictEqual(isDarkTheme, true); + + // Test with light theme + messageData.theme = "light"; + if (messageData.theme) { + isDarkTheme = messageData.theme === 'dark'; + } + + assert.strictEqual(isDarkTheme, false); + }); +}); diff --git a/src/webview/App.tsx b/src/webview/App.tsx index 0f807d8..8d9b3ce 100644 --- a/src/webview/App.tsx +++ b/src/webview/App.tsx @@ -10,6 +10,8 @@ import { ColumnAutoSizeModule, RowStyleModule, themeQuartz, + colorSchemeDark, + colorSchemeLight, RowStyle, RowClassParams, SizeColumnsToContentStrategy, @@ -42,9 +44,16 @@ export default function App() { const [rowData, setRowData] = useState([]); const [columnDefs, setColumnDefs] = useState([]); + const [isDarkTheme, setIsDarkTheme] = useState(false); const gridRef = useRef(null); + + // Create theme based on current theme state + const currentTheme = isDarkTheme + ? themeQuartz.withPart(colorSchemeDark) + : themeQuartz.withPart(colorSchemeLight); + const gridOptions: GridOptions = { - theme: themeQuartz, + theme: currentTheme, }; const [visibleColumns, setVisibleColumns] = useState([]); @@ -54,6 +63,12 @@ export default function App() { if (event.data?.type === "loadLogs") { console.log("[WEBVIEW] Message received:", event.data); const data = event.data.payload; + const theme = event.data.theme; + + // Update theme state + if (theme) { + setIsDarkTheme(theme === 'dark'); + } setRowData(data); if (data.length > 0) { @@ -110,11 +125,33 @@ export default function App() { return () => window.removeEventListener("message", listener); }, []); + // Container styles that adapt to theme + const containerStyle = { + height: "100vh", + width: "100%", + backgroundColor: isDarkTheme ? "#1e1e1e" : "#ffffff", + color: isDarkTheme ? "#ffffff" : "#000000" + }; + + const controlsStyle = { + padding: 10, + display: "flex", + flexWrap: "wrap" as const, + gap: 10, + backgroundColor: isDarkTheme ? "#2d2d2d" : "#f5f5f5", + borderBottom: `1px solid ${isDarkTheme ? "#404040" : "#e0e0e0"}` + }; + + const labelStyle = { + fontSize: "12px", + color: isDarkTheme ? "#ffffff" : "#000000" + }; + return ( -
-
+
+
{columnDefs.map((col) => ( -
); diff --git a/tsconfig.json b/tsconfig.json index 11e65b6..91c1b34 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,13 +10,16 @@ "outDir": "dist", "strict": true, "types": [ - "node" + "node", + "mocha", + "vscode" ], "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": [ - "src/extension.ts" + "src/extension.ts", + "src/test/**/*" ] } \ No newline at end of file