1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Merge branch 'master' into show-extension-preferences-in-separate-page

This commit is contained in:
Alex Andreev 2022-06-07 16:32:29 +03:00
commit 5cb9b69e1b
1475 changed files with 44670 additions and 33268 deletions

View File

@ -94,9 +94,25 @@ jobs:
GH_TOKEN: $(LENS_IDE_GH_TOKEN)
displayName: Customize config
- script: make build
- bash: |
set -e
echo "Importing codesign certificate ..."
echo $CSC_LINK | base64 -D > certificate.p12
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security set-keychain-settings -lut 21600 build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
security import certificate.p12 -k build.keychain -P $CSC_KEY_PASSWORD -T /usr/bin/codesign -T /usr/bin/security -A
security set-key-partition-list -S apple-tool:,apple: -k $KEYCHAIN_PASSWORD build.keychain
rm certificate.p12
echo "Codesign certificate imported!"
make build
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
env:
KEYCHAIN_PASSWORD: secretz
APPLEID: $(APPLEID)
APPLEIDPASS: $(APPLEIDPASS)
CSC_LINK: $(CSC_LINK)

View File

@ -22,15 +22,16 @@ module.exports = {
{
files: [
"**/*.js",
"**/*.mjs",
],
extends: [
"eslint:recommended",
],
env: {
node: true,
es2022: true,
},
parserOptions: {
ecmaVersion: 2018,
sourceType: "module",
},
plugins: [
@ -129,6 +130,14 @@ module.exports = {
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unused-vars": "off",
"no-restricted-imports": ["error", {
"paths": [
{
"name": ".",
"message": "No importing from local index.ts(x?) file. A common way to make circular dependencies.",
},
],
}],
"@typescript-eslint/member-delimiter-style": ["error", {
"multiline": {
"delimiter": "semi",
@ -139,6 +148,28 @@ module.exports = {
"requireLast": false,
},
}],
"react/jsx-max-props-per-line": ["error", {
"maximum": {
"single": 2,
"multi": 1,
},
}],
"react/jsx-first-prop-new-line": ["error", "multiline"],
"react/jsx-one-expression-per-line": ["error", {
"allow": "single-child",
}],
"react/jsx-indent": ["error", 2],
"react/jsx-indent-props": ["error", 2],
"react/jsx-closing-tag-location": "error",
"react/jsx-wrap-multilines": ["error", {
"declaration": "parens-new-line",
"assignment": "parens-new-line",
"return": "parens-new-line",
"arrow": "parens-new-line",
"condition": "parens-new-line",
"logical": "parens-new-line",
"prop": "parens-new-line",
}],
"react/display-name": "off",
"space-before-function-paren": "off",
"@typescript-eslint/space-before-function-paren": ["error", {
@ -217,5 +248,35 @@ module.exports = {
"@typescript-eslint/consistent-type-imports": "error",
},
},
{
files: [
"src/{common,main,renderer}/**/*.ts",
"src/{common,main,renderer}/**/*.tsx",
],
rules: {
"no-restricted-imports": ["error", {
"paths": [
{
"name": ".",
"message": "No importing from local index.ts(x?) file. A common way to make circular dependencies.",
},
{
"name": "..",
"message": "No importing from parent index.ts(x?) file. A common way to make circular dependencies.",
},
],
"patterns": [
{
"group": [
"**/extensions/renderer-api/**/*",
"**/extensions/main-api/**/*",
"**/extensions/common-api/**/*",
],
message: "No importing from the extension api definitions in application code",
},
],
}],
},
},
],
};

View File

@ -73,7 +73,9 @@ jobs:
- name: Install integration test dependencies
id: minikube
uses: medyagh/setup-minikube@5a9a7104d7322fa40424de8855c84685e89cefd7
uses: medyagh/setup-minikube@master
with:
minikube-version: latest
if: runner.os == 'Linux'
- run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration

1
.gitignore vendored
View File

@ -8,7 +8,6 @@ locales/**/**.js
lens.log
static/build
static/types
build/tray/
binaries/client/
binaries/server/
src/extensions/*/*.js

View File

@ -32,7 +32,7 @@ compile-dev: node_modules
ci-validate-dev: binaries/client build-extensions compile-dev
.PHONY: dev
dev: binaries/client build/tray/trayIconTemplate.png build-extensions
dev: binaries/client build-extensions
rm -rf static/build/
yarn dev
@ -53,7 +53,7 @@ integration: build
yarn integration
.PHONY: build
build: node_modules binaries/client build/tray/trayIconTemplate.png
build: node_modules binaries/client
yarn run npm:fix-build-version
$(MAKE) build-extensions -B
yarn run compile
@ -63,6 +63,10 @@ ifeq "$(DETECTED_OS)" "Windows"
endif
yarn run electron-builder --publish onTag $(ELECTRON_BUILDER_EXTRA_ARGS)
.PHONY: update-extension-locks
update-extension-locks:
$(foreach dir, $(extensions), (cd $(dir) && rm package-lock.json && ../../node_modules/.bin/npm install --package-lock-only);)
.NOTPARALLEL: $(extension_node_modules)
$(extension_node_modules): node_modules
cd $(@:/node_modules=) && ../../node_modules/.bin/npm install --no-audit --no-fund --no-save
@ -70,9 +74,6 @@ $(extension_node_modules): node_modules
$(extension_dists): src/extensions/npm/extensions/dist $(extension_node_modules)
cd $(@:/dist=) && ../../node_modules/.bin/npm run build
build/tray/trayIconTemplate.png: node_modules
yarn ts-node ./build/generate-tray-icons.ts
.PHONY: clean-old-extensions
clean-old-extensions:
find ./extensions -mindepth 1 -maxdepth 1 -type d '!' -exec test -e '{}/package.json' \; -exec rm -rf {} \;
@ -84,19 +85,17 @@ build-extensions: node_modules clean-old-extensions $(extension_dists)
test-extensions: $(extension_node_modules)
$(foreach dir, $(extensions), (cd $(dir) && npm run test || exit $?);)
.PHONY: copy-extension-themes
copy-extension-themes:
mkdir -p src/extensions/npm/extensions/dist/src/renderer/themes/
cp $(wildcard src/renderer/themes/*.json) src/extensions/npm/extensions/dist/src/renderer/themes/
src/extensions/npm/extensions/__mocks__:
cp -r __mocks__ src/extensions/npm/extensions/
src/extensions/npm/extensions/dist: node_modules
src/extensions/npm/extensions/dist: src/extensions/npm/extensions/node_modules
yarn compile:extension-types
src/extensions/npm/extensions/node_modules: src/extensions/npm/extensions/package.json
cd src/extensions/npm/extensions/ && ../../../../node_modules/.bin/npm install --no-audit --no-fund
.PHONY: build-npm
build-npm: build-extension-types copy-extension-themes src/extensions/npm/extensions/__mocks__
build-npm: build-extension-types src/extensions/npm/extensions/__mocks__
yarn npm:fix-package-version
.PHONY: build-extension-types
@ -129,7 +128,6 @@ clean: clean-npm clean-extensions
rm -rf binaries/client
rm -rf dist
rm -rf static/build
rm -rf build/tray
rm -rf node_modules
rm -rf site
rm -rf docs/extensions/api

View File

@ -1,7 +1,7 @@
# Lens Open Source Project (OpenLens)
[![Build Status](https://github.com/lensapp/lens/actions/workflows/test.yml/badge.svg)](https://github.com/lensapp/lens/actions/workflows/test.yml)
[![Chat on Slack](https://img.shields.io/badge/chat-on%20slack-blue.svg?logo=slack&longCache=true&style=flat)](https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI)
[![Chat on Slack](https://img.shields.io/badge/chat-on%20slack-blue.svg?logo=slack&longCache=true&style=flat)](https://join.slack.com/t/k8slens/shared_invite/zt-198iepl92-EPJsCckkJ~f887vWqJcgGA)
## The Repository

View File

@ -0,0 +1,5 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export default {};

View File

@ -0,0 +1,5 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export default {};

View File

@ -5,11 +5,11 @@
import fs from "fs-extra";
import path from "path";
import defaultBaseLensTheme from "../src/renderer/themes/lens-dark.json";
import defaultBaseLensTheme from "../src/renderer/themes/lens-dark";
const outputCssFile = path.resolve("src/renderer/themes/theme-vars.css");
const banner = `/*
const banner = `/*
Generated Lens theme CSS-variables, don't edit manually.
To refresh file run $: yarn run ts-node build/${path.basename(__filename)}
*/`;

View File

@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import packageInfo from "../package.json";
import { type WriteStream } from "fs";
import type { FileHandle } from "fs/promises";
import { open } from "fs/promises";
import type { WriteStream } from "fs-extra";
import { constants, ensureDir, unlink } from "fs-extra";
import path from "path";
import fetch from "node-fetch";
@ -17,6 +17,7 @@ import AbortController from "abort-controller";
import { extract } from "tar-stream";
import gunzip from "gunzip-maybe";
import { getBinaryName, normalizedPlatform } from "../src/common/vars";
import { isErrnoException } from "../src/common/utils";
const pipeline = promisify(_pipeline);
@ -44,6 +45,10 @@ abstract class BinaryDownloader {
}
async ensureBinary(): Promise<void> {
if (process.env.LENS_SKIP_DOWNLOAD_BINARIES === "true") {
return;
}
const controller = new AbortController();
const stream = await fetch(this.url, {
timeout: 15 * 60 * 1000, // 15min
@ -51,7 +56,7 @@ abstract class BinaryDownloader {
});
const total = Number(stream.headers.get("content-length"));
const bar = this.bar;
let fileHandle: FileHandle;
let fileHandle: FileHandle | undefined = undefined;
if (isNaN(total)) {
throw new Error("no content-length header was present");
@ -66,7 +71,7 @@ abstract class BinaryDownloader {
* This is necessary because for some reason `createWriteStream({ flags: "wx" })`
* was throwing someplace else and not here
*/
fileHandle = await open(this.target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL);
const handle = fileHandle = await open(this.target, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL);
await pipeline(
stream.body,
@ -79,7 +84,7 @@ abstract class BinaryDownloader {
}),
...this.getTransformStreams(new Writable({
write(chunk, encoding, cb) {
fileHandle.write(chunk)
handle.write(chunk)
.then(() => cb())
.catch(cb);
},
@ -90,7 +95,7 @@ abstract class BinaryDownloader {
} catch (error) {
await fileHandle?.close();
if (error.code === "EEXIST") {
if (isErrnoException(error) && error.code === "EEXIST") {
bar.increment(total); // mark as finished
controller.abort(); // stop trying to download
} else {

View File

@ -28,23 +28,23 @@ console.log("Generating tray icon pngs");
ensureDirSync(outputFolder);
Promise.allSettled([
sharp(Buffer.from(darkTemplate))
Promise.all([
sharp(Buffer.from(lightTemplate))
.resize({ width: size, height: size })
.png()
.toFile(path.join(outputFolder, "trayIconDarkTemplate.png")),
sharp(Buffer.from(darkTemplate))
sharp(Buffer.from(lightTemplate))
.resize({ width: size*2, height: size*2 })
.png()
.toFile(path.join(outputFolder, "trayIconDarkTemplate@2x.png")),
sharp(Buffer.from(lightTemplate))
sharp(Buffer.from(darkTemplate))
.resize({ width: size, height: size })
.png()
.toFile(path.join(outputFolder, "trayIconTemplate.png")),
sharp(Buffer.from(lightTemplate))
sharp(Buffer.from(darkTemplate))
.resize({ width: size*2, height: size*2 })
.png()
.toFile(path.join(outputFolder, "trayIconTemplate@2x.png")),
])
.then(console.log)
.then((resolutions) => console.log(`Generated ${resolutions.length} images`))
.catch(console.error);

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

6
build/tsconfig.json Normal file
View File

@ -0,0 +1,6 @@
{
"extends": "../tsconfig.json",
"include": [
"./**/*",
]
}

View File

@ -79,7 +79,7 @@ Some of the most-important fields include:
}
```
## Webpack configuation
## Webpack configuration
The following webpack `externals` are provided by `Lens` and must be used (when available) to make sure that the versions used are in sync.

View File

@ -224,7 +224,7 @@ export default class ExampleExtension extends Renderer.LensExtension {
{
id: "bonjour",
components: {
Page: () => <ExemplePage extension={this} />,
Page: () => <ExamplePage extension={this} />,
},
},
];
@ -250,7 +250,7 @@ export default class ExampleExtension extends Renderer.LensExtension {
target: { pageId: "bonjour" },
title: "Bonjour le monde",
components: {
Icon: ExempleIcon,
Icon: ExampleIcon,
},
},
];

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,11 +24,6 @@ spec:
operator: In
values:
- linux
- matchExpressions:
- key: beta.kubernetes.io/os
operator: In
values:
- linux
# <%- if config.node_selector -%>
# nodeSelector:
# <%- node_selector.to_h.each do |key, value| -%>

View File

@ -30,11 +30,6 @@ spec:
operator: In
values:
- linux
- matchExpressions:
- key: beta.kubernetes.io/os
operator: In
values:
- linux
securityContext:
runAsNonRoot: true
runAsUser: 65534

View File

@ -23,11 +23,6 @@ spec:
operator: In
values:
- linux
- matchExpressions:
- key: beta.kubernetes.io/os
operator: In
values:
- linux
serviceAccountName: kube-state-metrics
containers:
- name: kube-state-metrics

View File

@ -208,14 +208,14 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
<section>
<SubTitle title="Prometheus" />
<FormSwitch
control={
control={(
<Switcher
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
checked={!!this.featureStates.prometheus && this.props.cluster.status.phase == "connected"}
onChange={v => this.togglePrometheus(v.target.checked)}
name="prometheus"
/>
}
)}
label="Enable bundled Prometheus metrics stack"
/>
<small className="hint">
@ -226,14 +226,14 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
<section>
<SubTitle title="Kube State Metrics" />
<FormSwitch
control={
control={(
<Switcher
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.phase == "connected"}
onChange={v => this.toggleKubeStateMetrics(v.target.checked)}
name="node-exporter"
/>
}
)}
label="Enable bundled kube-state-metrics stack"
/>
<small className="hint">
@ -245,14 +245,14 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
<section>
<SubTitle title="Node Exporter" />
<FormSwitch
control={
control={(
<Switcher
disabled={this.featureStates.nodeExporter === undefined || !this.isTogglable}
checked={!!this.featureStates.nodeExporter && this.props.cluster.status.phase == "connected"}
onChange={v => this.toggleNodeExporter(v.target.checked)}
name="node-exporter"
/>
}
)}
label="Enable bundled node-exporter stack"
/>
<small className="hint">
@ -271,9 +271,11 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
className="w-60 h-14"
/>
{this.canUpgrade && (<small className="hint">
An update is available for enabled metrics components.
</small>)}
{this.canUpgrade && (
<small className="hint">
An update is available for enabled metrics components.
</small>
)}
</section>
</>
);

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,9 @@ export function NodeMenu(props: NodeMenuProps) {
labelOk: `Drain Node`,
message: (
<p>
Are you sure you want to drain <b>{nodeName}</b>?
{"Are you sure you want to drain "}
<b>{nodeName}</b>
?
</p>
),
});
@ -77,26 +79,42 @@ export function NodeMenu(props: NodeMenuProps) {
return (
<>
<MenuItem onClick={shell}>
<Icon svg="ssh" interactive={toolbar} tooltip={toolbar && "Node shell"}/>
<Icon
svg="ssh"
interactive={toolbar}
tooltip={toolbar && "Node shell"}
/>
<span className="title">Shell</span>
</MenuItem>
{
node.isUnschedulable()
? (
<MenuItem onClick={unCordon}>
<Icon material="play_circle_filled" tooltip={toolbar && "Uncordon"} interactive={toolbar} />
<Icon
material="play_circle_filled"
tooltip={toolbar && "Uncordon"}
interactive={toolbar}
/>
<span className="title">Uncordon</span>
</MenuItem>
)
: (
<MenuItem onClick={cordon}>
<Icon material="pause_circle_filled" tooltip={toolbar && "Cordon"} interactive={toolbar} />
<Icon
material="pause_circle_filled"
tooltip={toolbar && "Cordon"}
interactive={toolbar}
/>
<span className="title">Cordon</span>
</MenuItem>
)
}
<MenuItem onClick={drain}>
<Icon material="delete_sweep" tooltip={toolbar && "Drain"} interactive={toolbar}/>
<Icon
material="delete_sweep"
tooltip={toolbar && "Drain"}
interactive={toolbar}
/>
<span className="title">Drain</span>
</MenuItem>
</>

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,11 @@ export class PodAttachMenu extends React.Component<PodAttachMenuProps> {
return (
<MenuItem onClick={Util.prevDefault(() => this.attachToPod(containers[0].name))}>
<Icon material="pageview" interactive={toolbar} tooltip={toolbar && "Attach to Pod"}/>
<Icon
material="pageview"
interactive={toolbar}
tooltip={toolbar && "Attach to Pod"}
/>
<span className="title">Attach Pod</span>
{containers.length > 1 && (
<>
@ -82,7 +86,11 @@ export class PodAttachMenu extends React.Component<PodAttachMenuProps> {
const { name } = container;
return (
<MenuItem key={name} onClick={Util.prevDefault(() => this.attachToPod(name))} className="flex align-center">
<MenuItem
key={name}
onClick={Util.prevDefault(() => this.attachToPod(name))}
className="flex align-center"
>
<StatusBrick/>
<span>{name}</span>
</MenuItem>

View File

@ -46,7 +46,11 @@ export class PodLogsMenu extends React.Component<PodLogsMenuProps> {
return (
<MenuItem onClick={Util.prevDefault(() => this.showLogs(containers[0]))}>
<Icon material="subject" interactive={toolbar} tooltip={toolbar && "Pod Logs"}/>
<Icon
material="subject"
interactive={toolbar}
tooltip={toolbar && "Pod Logs"}
/>
<span className="title">Logs</span>
{containers.length > 1 && (
<>
@ -63,7 +67,11 @@ export class PodLogsMenu extends React.Component<PodLogsMenuProps> {
) : null;
return (
<MenuItem key={name} onClick={Util.prevDefault(() => this.showLogs(container))} className="flex align-center">
<MenuItem
key={name}
onClick={Util.prevDefault(() => this.showLogs(container))}
className="flex align-center"
>
{brick}
<span>{name}</span>
</MenuItem>

View File

@ -79,7 +79,11 @@ export class PodShellMenu extends React.Component<PodShellMenuProps> {
return (
<MenuItem onClick={Util.prevDefault(() => this.execShell(containers[0].name))}>
<Icon svg="ssh" interactive={toolbar} tooltip={toolbar && "Pod Shell"} />
<Icon
svg="ssh"
interactive={toolbar}
tooltip={toolbar && "Pod Shell"}
/>
<span className="title">Shell</span>
{containers.length > 1 && (
<>
@ -90,7 +94,11 @@ export class PodShellMenu extends React.Component<PodShellMenuProps> {
const { name } = container;
return (
<MenuItem key={name} onClick={Util.prevDefault(() => this.execShell(name))} className="flex align-center">
<MenuItem
key={name}
onClick={Util.prevDefault(() => this.execShell(name))}
className="flex align-center"
>
<StatusBrick/>
<span>{name}</span>
</MenuItem>

View File

@ -39,14 +39,14 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
await frame.waitForSelector(`.Menu >> text="Remove"`);
});
it("opens cluster settings by clicking link in no-metrics area", async () => {
// FIXME: failed locally since metrics might already exist, cc @aleksfront
it.skip("opens cluster settings by clicking link in no-metrics area", async () => {
await frame.locator("text=Open cluster settings >> nth=0").click();
await window.waitForSelector(`[data-testid="metrics-header"]`);
});
it(
"should navigate around common cluster pages",
async () => {
const scenariosByParent = pipeline(
scenarios,
@ -138,7 +138,7 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
);
it(
`should create the ${TEST_NAMESPACE} and a pod in the namespace`,
`should create the ${TEST_NAMESPACE} and a pod in the namespace and then remove that pod via the context menu`,
async () => {
await navigateToNamespaces(frame);
await frame.click("button.add-button");
@ -208,6 +208,10 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
await frame.click(".Dock .Button >> text='Create'");
await frame.waitForSelector(`.TableCell >> text=${testPodName}`);
await frame.click(".TableRow .TableCell.menu");
await frame.click(".MenuItem >> text=Delete");
await frame.click("button >> text=Remove");
await frame.waitForSelector(`.TableCell >> text=${testPodName}`, { state: "detached" });
},
10 * 60 * 1000,
);

View File

@ -24,9 +24,9 @@ describe("Lens command palette", () => {
utils.itIf(!isWindows)("opens command dialog from menu", async () => {
await app.evaluate(async ({ app }) => {
await app.applicationMenu
.getMenuItemById("view")
.submenu.getMenuItemById("command-palette")
.click();
?.getMenuItemById("view")
?.submenu?.getMenuItemById("command-palette")
?.click();
});
await window.waitForSelector(".Select__option >> text=Hotbar: Switch");
}, 10*60*1000);

View File

@ -108,6 +108,10 @@ export async function lauchMinikubeClusterFromCatalog(window: Page): Promise<Fra
const frame = await minikubeFrame.contentFrame();
if (!frame) {
throw new Error("No iframe for minikube found");
}
await frame.waitForSelector("[data-testid=cluster-sidebar]");
return frame;

View File

@ -0,0 +1,6 @@
{
"extends": "../tsconfig.json",
"include": [
"./**/*",
]
}

View File

@ -3,7 +3,7 @@
"productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes",
"homepage": "https://github.com/lensapp/lens",
"version": "5.5.0-beta.0",
"version": "5.6.0-alpha.0",
"main": "static/build/main.js",
"copyright": "© 2021 OpenLens Authors",
"license": "MIT",
@ -21,19 +21,19 @@
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
"compile:main": "yarn run webpack --config webpack/main.ts",
"compile:renderer": "yarn run webpack --config webpack/renderer.ts",
"compile:extension-types": "yarn run webpack --config webpack/extensions.ts",
"compile:extension-types": "yarn run tsc --project tsconfig.extension-api.json",
"npm:fix-build-version": "yarn run ts-node build/set_build_version.ts",
"npm:fix-package-version": "yarn run ts-node build/set_npm_version.ts",
"build:linux": "yarn run compile && electron-builder --linux --dir",
"build:mac": "yarn run compile && electron-builder --mac --dir",
"build:win": "yarn run compile && electron-builder --win --dir",
"integration": "jest --runInBand --detectOpenHandles --forceExit integration",
"test:unit": "jest --watch --testPathIgnorePatterns integration",
"test:unit": "func() { jest ${1} --watch --testPathIgnorePatterns integration; }; func",
"test:integration": "func() { jest ${1:-xyz} --watch --runInBand --detectOpenHandles --forceExit --modulePaths=[\"<rootDir>/integration/\"]; }; func",
"dist": "yarn run compile && electron-builder --publish onTag",
"dist:dir": "yarn run dist --dir -c.compression=store -c.mac.identity=null",
"download:binaries": "yarn run ts-node build/download_binaries.ts",
"build:tray-icons": "yarn run ts-node build/build_tray_icon.ts",
"build:tray-icons": "yarn run ts-node build/generate-tray-icons.ts",
"build:theme-vars": "yarn run ts-node build/build_theme_vars.ts",
"lint": "PROD=true yarn run eslint --ext js,ts,tsx --max-warnings=0 .",
"lint:fix": "yarn run lint --fix",
@ -72,6 +72,9 @@
"<rootDir>/src/jest.setup.ts",
"jest-canvas-mock"
],
"setupFilesAfterEnv": [
"<rootDir>/src/jest-after-env.setup.ts"
],
"globals": {
"ts-jest": {
"isolatedModules": true
@ -203,9 +206,11 @@
"@hapi/call": "^8.0.1",
"@hapi/subtext": "^7.0.3",
"@kubernetes/client-node": "^0.16.3",
"@ogre-tools/fp": "5.2.0",
"@ogre-tools/injectable": "5.2.0",
"@ogre-tools/injectable-react": "5.2.0",
"@material-ui/styles": "^4.11.5",
"@ogre-tools/injectable": "7.0.0",
"@ogre-tools/injectable-react": "7.0.0",
"@ogre-tools/fp": "7.0.0",
"@ogre-tools/injectable-extension-for-auto-registration": "7.0.0",
"@sentry/electron": "^3.0.7",
"@sentry/integrations": "^6.19.3",
"@types/circular-dependency-plugin": "5.0.5",
@ -222,21 +227,22 @@
"filehound": "^1.17.6",
"fs-extra": "^9.0.1",
"glob-to-regexp": "^0.4.1",
"got": "^11.8.3",
"got": "^11.8.5",
"grapheme-splitter": "^1.0.4",
"handlebars": "^4.7.7",
"history": "^4.10.1",
"http-proxy": "^1.18.1",
"immer": "^9.0.12",
"immer": "^9.0.14",
"joi": "^17.6.0",
"js-yaml": "^4.1.0",
"jsdom": "^16.7.0",
"lodash": "^4.17.15",
"mac-ca": "^1.0.6",
"marked": "^4.0.15",
"marked": "^4.0.16",
"md5-file": "^5.0.0",
"mobx": "^6.5.0",
"mobx-observable-history": "^2.0.3",
"mobx-react": "^7.3.0",
"mobx-react": "^7.5.0",
"mobx-utils": "^6.0.4",
"mock-fs": "^5.1.2",
"moment": "^2.29.3",
@ -265,35 +271,38 @@
"tar": "^6.1.11",
"tcp-port-used": "^1.0.2",
"tempy": "1.0.1",
"typed-regex": "^0.0.8",
"url-parse": "^1.5.10",
"uuid": "^8.3.2",
"win-ca": "^3.5.0",
"winston": "^3.7.2",
"winston-console-format": "^1.0.8",
"winston-transport-browserconsole": "^1.0.5",
"ws": "^7.5.7"
"ws": "^8.7.0"
},
"devDependencies": {
"@async-fn/jest": "1.5.3",
"@async-fn/jest": "1.6.1",
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"@sentry/types": "^6.19.7",
"@testing-library/dom": "^7.31.2",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^12.1.4",
"@testing-library/react": "^12.1.5",
"@testing-library/user-event": "^13.5.0",
"@types/byline": "^4.2.33",
"@types/chart.js": "^2.9.36",
"@types/cli-progress": "^3.9.2",
"@types/circular-dependency-plugin": "5.0.5",
"@types/cli-progress": "^3.11.0",
"@types/color": "^3.0.3",
"@types/command-line-args": "^5.2.0",
"@types/crypto-js": "^3.1.47",
"@types/dompurify": "^2.3.3",
"@types/electron-devtools-installer": "^2.2.1",
"@types/fs-extra": "^9.0.13",
"@types/glob-to-regexp": "^0.4.1",
"@types/gunzip-maybe": "^1.4.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/html-webpack-plugin": "^3.2.6",
"@types/http-proxy": "^1.17.9",
"@types/jest": "^26.0.24",
@ -304,17 +313,17 @@
"@types/md5-file": "^4.0.2",
"@types/mini-css-extract-plugin": "^2.4.0",
"@types/mock-fs": "^4.13.1",
"@types/node": "14.18.12",
"@types/node": "14.18.18",
"@types/node-fetch": "^2.6.1",
"@types/npm": "^2.0.32",
"@types/proper-lockfile": "^4.1.2",
"@types/randomcolor": "^0.5.6",
"@types/react": "^17.0.44",
"@types/react": "^17.0.45",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-dom": "^17.0.14",
"@types/react-dom": "^17.0.16",
"@types/react-router": "^5.1.18",
"@types/react-router-dom": "^5.3.3",
"@types/react-select": "3.1.2",
"@types/react-table": "^7.7.11",
"@types/react-table": "^7.7.12",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
"@types/readable-stream": "^2.3.13",
@ -332,34 +341,34 @@
"@types/uuid": "^8.3.4",
"@types/webpack": "^5.28.0",
"@types/webpack-dev-server": "^4.7.2",
"@types/webpack-env": "^1.16.3",
"@types/webpack-env": "^1.17.0",
"@types/webpack-node-externals": "^2.5.3",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.17.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"ansi_up": "^5.1.0",
"chart.js": "^2.9.4",
"circular-dependency-plugin": "^5.2.2",
"cli-progress": "^3.11.0",
"cli-progress": "^3.11.1",
"color": "^3.2.1",
"concurrently": "^7.1.0",
"command-line-args": "^5.2.1",
"concurrently": "^7.2.1",
"css-loader": "^6.7.1",
"deepdash": "^5.3.9",
"dompurify": "^2.3.6",
"dompurify": "^2.3.8",
"electron": "^14.2.9",
"electron-builder": "^23.0.3",
"electron-notarize": "^0.3.0",
"esbuild": "^0.14.38",
"esbuild-loader": "^2.18.0",
"eslint": "^8.14.0",
"esbuild-loader": "^2.19.0",
"eslint": "^8.16.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.5.0",
"eslint-plugin-unused-imports": "^2.0.0",
"flex.box": "^3.4.4",
"fork-ts-checker-webpack-plugin": "^6.5.0",
"fork-ts-checker-webpack-plugin": "^6.5.2",
"gunzip-maybe": "^1.4.2",
"hoist-non-react-statics": "^3.3.2",
"html-webpack-plugin": "^5.5.0",
"identity-obj-proxy": "^3.0.0",
"ignore-loader": "^0.1.2",
@ -374,37 +383,36 @@
"node-gyp": "7.1.2",
"node-loader": "^2.0.0",
"nodemon": "^2.0.16",
"playwright": "^1.20.2",
"postcss": "^8.4.12",
"playwright": "^1.22.2",
"postcss": "^8.4.14",
"postcss-loader": "^6.2.1",
"randomcolor": "^0.6.2",
"react-beautiful-dnd": "^13.1.0",
"react-refresh": "^0.12.0",
"react-refresh-typescript": "^2.0.4",
"react-router-dom": "^5.3.1",
"react-select": "^5.3.0",
"react-refresh": "^0.13.0",
"react-refresh-typescript": "^2.0.5",
"react-router-dom": "^5.3.3",
"react-select": "^5.3.2",
"react-select-event": "^5.5.0",
"react-table": "^7.7.0",
"react-window": "^1.8.6",
"sass": "^1.51.0",
"react-table": "^7.8.0",
"react-window": "^1.8.7",
"sass": "^1.52.2",
"sass-loader": "^12.6.0",
"sharp": "^0.30.4",
"sharp": "^0.30.6",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.23",
"tar-stream": "^2.2.0",
"ts-jest": "26.5.6",
"ts-loader": "^9.2.8",
"ts-node": "^10.7.0",
"type-fest": "^2.12.2",
"type-fest": "^2.13.0",
"typed-emitter": "^1.4.0",
"typedoc": "0.22.15",
"typedoc": "0.22.17",
"typedoc-plugin-markdown": "^3.11.12",
"typeface-roboto": "^1.1.13",
"typescript": "^4.5.5",
"typescript-plugin-css-modules": "^3.4.0",
"webpack": "^5.72.0",
"webpack": "^5.73.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.1",
"webpack-dev-server": "^4.9.1",
"webpack-node-externals": "^3.0.0",
"xterm": "^4.18.0",
"xterm-addon-fit": "^0.5.0"

210
scripts/clear-release-pr.mjs Executable file
View File

@ -0,0 +1,210 @@
#!/usr/bin/env node
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
// This script creates a release PR
import { execSync, exec, spawn } from "child_process";
import commandLineArgs from "command-line-args";
import fse from "fs-extra";
import { basename } from "path";
import semver from "semver";
import { promisify } from "util";
const {
SemVer,
valid: semverValid,
rcompare: semverRcompare,
lte: semverLte,
} = semver;
const { readJsonSync } = fse;
const execP = promisify(exec);
const options = commandLineArgs([
{
name: "type",
defaultOption: true,
},
{
name: "preid",
},
]);
const validReleaseValues = [
"major",
"minor",
"patch",
];
const validPrereleaseValues = [
"premajor",
"preminor",
"prepatch",
"prerelease",
];
const validPreidValues = [
"alpha",
"beta",
];
const errorMessages = {
noReleaseType: `No release type provided. Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`,
invalidRelease: (invalid) => `Invalid release type was provided (value was "${invalid}"). Valid options are: ${[...validReleaseValues, ...validPrereleaseValues].join(", ")}`,
noPreid: `No preid was provided. Use '--preid' to specify. Valid options are: ${validPreidValues.join(", ")}`,
invalidPreid: (invalid) => `Invalid preid was provided (value was "${invalid}"). Valid options are: ${validPreidValues.join(", ")}`,
wrongCwd: "It looks like you are running this script from the 'scripts' directory. This script assumes it is run from the root of the git repo",
};
if (!options.type) {
console.error(errorMessages.noReleaseType);
process.exit(1);
}
if (validReleaseValues.includes(options.type)) {
// do nothing, is valid
} else if (validPrereleaseValues.includes(options.type)) {
if (!options.preid) {
console.error(errorMessages.noPreid);
process.exit(1);
}
if (!validPreidValues.includes(options.preid)) {
console.error(errorMessages.invalidPreid(options.preid));
process.exit(1);
}
} else {
console.error(errorMessages.invalidRelease(options.type));
process.exit(1);
}
if (basename(process.cwd()) === "scripts") {
console.error(errorMessages.wrongCwd);
}
const currentVersion = new SemVer(readJsonSync("./package.json").version);
const currentVersionMilestone = `${currentVersion.major}.${currentVersion.minor}.${currentVersion.patch}`;
console.log(`current version: ${currentVersion.format()}`);
console.log("fetching tags...");
execSync("git fetch --tags --force");
const actualTags = execSync("git tag --list", { encoding: "utf-8" }).split(/\r?\n/).map(line => line.trim());
const [previousReleasedVersion] = actualTags
.map(semverValid)
.filter(Boolean)
.sort(semverRcompare)
.filter(version => semverLte(version, currentVersion));
const npmVersionArgs = [
"npm",
"version",
options.type,
];
if (options.preid) {
npmVersionArgs.push(`--preid=${options.preid}`);
}
npmVersionArgs.push("--git-tag-version false");
execSync(npmVersionArgs.join(" "), { stdio: "ignore" });
const newVersion = new SemVer(readJsonSync("./package.json").version);
const getMergedPrsArgs = [
"gh",
"pr",
"list",
"--limit=500", // Should be big enough, if not we need to release more often ;)
"--state=merged",
"--base=master",
"--json mergeCommit,title,author,labels,number,milestone",
];
console.log("retreiving last 500 PRs to create release PR body...");
const mergedPrs = JSON.parse(execSync(getMergedPrsArgs.join(" "), { encoding: "utf-8" }));
const milestoneRelevantPrs = mergedPrs.filter(pr => pr.milestone && pr.milestone.title === currentVersionMilestone);
const relaventPrsQuery = await Promise.all(
milestoneRelevantPrs.map(async pr => ({
pr,
stdout: (await execP(`git tag v${previousReleasedVersion} --no-contains ${pr.mergeCommit.oid}`)).stdout,
})),
);
const relaventPrs = relaventPrsQuery
.filter(query => query.stdout)
.map(query => query.pr);
const enhancementPrLabelName = "enhancement";
const bugfixPrLabelName = "bug";
const enhancementPrs = relaventPrs.filter(pr => pr.labels.some(label => label.name === enhancementPrLabelName));
const bugfixPrs = relaventPrs.filter(pr => pr.labels.some(label => label.name === bugfixPrLabelName));
const maintenencePrs = relaventPrs.filter(pr => pr.labels.every(label => label.name !== bugfixPrLabelName && label.name !== enhancementPrLabelName));
console.log("Found:");
console.log(`${enhancementPrs.length} enhancement PRs`);
console.log(`${bugfixPrs.length} bug fix PRs`);
console.log(`${maintenencePrs.length} maintenence PRs`);
const prBodyLines = [
`## Changes since ${previousReleasedVersion}`,
"",
];
if (enhancementPrs.length > 0) {
prBodyLines.push(
"## 🚀 Features",
"",
...enhancementPrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`),
"",
);
}
if (bugfixPrs.length > 0) {
prBodyLines.push(
"## 🐛 Bug Fixes",
"",
...bugfixPrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`),
"",
);
}
if (maintenencePrs.length > 0) {
prBodyLines.push(
"## 🧰 Maintenance",
"",
...maintenencePrs.map(pr => `- ${pr.title} (**#${pr.number}**) https://github.com/${pr.author.login}`),
"",
);
}
const prBody = prBodyLines.join("\n");
const prBase = newVersion.patch === 0
? "master"
: `release/v${newVersion.major}.${newVersion.minor}`;
const createPrArgs = [
"pr",
"create",
"--base", prBase,
"--title", `release ${newVersion.format()}`,
"--label", "skip-changelog",
"--body-file", "-",
];
const createPrProcess = spawn("gh", createPrArgs, { stdio: "pipe" });
let result = "";
createPrProcess.stdout.on("data", (chunk) => result += chunk);
createPrProcess.stdin.write(prBody);
createPrProcess.stdin.end();
await new Promise((resolve) => {
createPrProcess.on("close", () => {
createPrProcess.stdout.removeAllListeners();
resolve();
});
});
console.log(result);

View File

@ -1,11 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`extension special characters in page registrations renders 1`] = `<div />`;
exports[`extension special characters in page registrations renders 1`] = `
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
exports[`extension special characters in page registrations when navigating to route with ID having special characters renders 1`] = `
<div>
<div>
Some page
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -1,12 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`navigate to extension page renders 1`] = `<div />`;
exports[`navigate to extension page renders 1`] = `
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
exports[`navigate to extension page when extension navigates to child route renders 1`] = `
<div>
<div>
Child page
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -31,6 +40,9 @@ exports[`navigate to extension page when extension navigates to route with param
Some button
</button>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -55,6 +67,9 @@ exports[`navigate to extension page when extension navigates to route without pa
Some button
</button>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -79,5 +94,8 @@ exports[`navigate to extension page when extension navigates to route without pa
Some button
</button>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -8,6 +8,9 @@ exports[`navigating between routes given route with optional path parameters whe
"someOtherParameter": "some-other-value"
}
</pre>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -16,5 +19,8 @@ exports[`navigating between routes given route without path parameters when navi
<div>
Some component
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -1,6 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`add-cluster - navigation using application menu renders 1`] = `<div />`;
exports[`add-cluster - navigation using application menu renders 1`] = `
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
exports[`add-cluster - navigation using application menu when navigating to add cluster using application menu renders 1`] = `
<div>
@ -19,7 +25,7 @@ exports[`add-cluster - navigation using application menu when navigating to add
Add Clusters from Kubeconfig
</h2>
<p>
Clusters added here are
Clusters added here are
<b>
not
</b>
@ -27,16 +33,14 @@ exports[`add-cluster - navigation using application menu when navigating to add
<code>
~/.kube/config
</code>
file.
file.
<a
href="https://docs.k8slens.dev/main//catalog/add-clusters/"
rel="noreferrer"
target="_blank"
>
Read more about adding clusters
Read more about adding clusters.
</a>
.
</p>
<div
class="flex column"
@ -87,5 +91,8 @@ exports[`add-cluster - navigation using application menu when navigating to add
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -6,20 +6,27 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
import React from "react";
// TODO: Make components free of side effects by making them deterministic
jest.mock("../../renderer/components/tooltip");
jest.mock("../../renderer/components/monaco-editor/monaco-editor");
jest.mock("../../renderer/components/tooltip/tooltip", () => ({
Tooltip: () => null,
}));
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
withTooltip: (Target: any) => ({ tooltip, tooltipOverrideDisabled, ...props }: any) => <Target {...props} />,
}));
jest.mock("../../renderer/components/monaco-editor/monaco-editor", () => ({
MonacoEditor: () => null,
}));
describe("add-cluster - navigation using application menu", () => {
let applicationBuilder: ApplicationBuilder;
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder = getApplicationBuilder().beforeSetups(({ mainDi }) => {
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
});
applicationBuilder = getApplicationBuilder();
rendered = await applicationBuilder.render();
});
@ -35,8 +42,8 @@ describe("add-cluster - navigation using application menu", () => {
});
describe("when navigating to add cluster using application menu", () => {
beforeEach(() => {
applicationBuilder.applicationMenu.click("file.add-cluster");
beforeEach(async () => {
await applicationBuilder.applicationMenu.click("file.add-cluster");
});
it("renders", () => {

View File

@ -0,0 +1,536 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`installing update using tray when started renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update using tray when started when user checks for updates using tray renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Checking for updates...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_13"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
</div>
</body>
`;
exports[`installing update using tray when started when user checks for updates using tray when new update is discovered renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Checking for updates...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_96"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Download for version some-version started...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_99"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
</div>
</body>
`;
exports[`installing update using tray when started when user checks for updates using tray when new update is discovered when download fails renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Checking for updates...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_149"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Download for version some-version started...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_152"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Download of update failed
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_157"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
</div>
</body>
`;
exports[`installing update using tray when started when user checks for updates using tray when new update is discovered when download succeeds renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Checking for updates...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_215"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Download for version some-version started...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_218"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
<div
class="flex column gaps"
data-testid="ask-boolean-some-irrelevant-random-id"
>
<b>
Update Available
</b>
<p>
Version some-version of Lens IDE is available and ready to be installed. Would you like to update now?
Lens should restart automatically, if it doesn't please restart manually. Installed extensions might require updating.
</p>
<div
class="flex gaps row align-left box grow"
>
<button
class="Button light"
data-testid="ask-boolean-some-irrelevant-random-id-button-yes"
type="button"
>
Yes
</button>
<button
class="Button active outlined"
data-testid="ask-boolean-some-irrelevant-random-id-button-no"
type="button"
>
No
</button>
</div>
</div>
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-ask-boolean-for-some-irrelevant-random-id"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
</div>
</body>
`;
exports[`installing update using tray when started when user checks for updates using tray when no new update is discovered renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
Checking for updates...
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_48"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
<div
class="Animate opacity notification flex info enter"
style="--enter-duration: 100ms; --leave-duration: 100ms;"
>
<div
class="box"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="info_outline"
>
info_outline
</span>
</i>
</div>
<div
class="message box grow"
>
No new updates available
</div>
<div
class="box"
>
<i
class="Icon close material interactive focusable"
data-testid="close-notification-for-notification_51"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
</div>
</div>
</div>
</body>
`;

View File

@ -0,0 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`installing update when started renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered when download fails renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds when user answers not to install the update renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds when user answers to install the update renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`installing update when started when user checks for updates when no new update is discovered renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;

View File

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`periodical checking of updates given updater is enabled and configuration exists, when started renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;

View File

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`selection of update stability when started renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;

View File

@ -0,0 +1,87 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
import selectedUpdateChannelInjectable from "../../common/application-update/selected-update-channel/selected-update-channel.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable";
import { updateChannels } from "../../common/application-update/update-channels";
describe("downgrading version update", () => {
let applicationBuilder: ApplicationBuilder;
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
let mainDi: DiContainer;
beforeEach(() => {
jest.useFakeTimers();
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
checkForPlatformUpdatesMock = asyncFn();
mainDi.override(
checkForPlatformUpdatesInjectable,
() => checkForPlatformUpdatesMock,
);
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
mainDi.override(publishIsConfiguredInjectable, () => true);
});
mainDi = applicationBuilder.dis.mainDi;
});
[
{
updateChannel: updateChannels.latest,
appVersion: "4.0.0-beta",
downgradeIsAllowed: true,
},
{
updateChannel: updateChannels.beta,
appVersion: "4.0.0-beta",
downgradeIsAllowed: false,
},
{
updateChannel: updateChannels.beta,
appVersion: "4.0.0-beta.1",
downgradeIsAllowed: false,
},
{
updateChannel: updateChannels.alpha,
appVersion: "4.0.0-beta",
downgradeIsAllowed: true,
},
{
updateChannel: updateChannels.alpha,
appVersion: "4.0.0-alpha",
downgradeIsAllowed: false,
},
].forEach(({ appVersion, updateChannel, downgradeIsAllowed }) => {
it(`given application version "${appVersion}" and update channel "${updateChannel.id}", when checking for updates, can${downgradeIsAllowed ? "": "not"} downgrade`, async () => {
mainDi.override(appVersionInjectable, () => appVersion);
await applicationBuilder.render();
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
selectedUpdateChannel.setValue(updateChannel.id);
const processCheckingForUpdates = mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(expect.any(Object), { allowDowngrade: downgradeIsAllowed });
});
});
});

View File

@ -0,0 +1,235 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import type { RenderResult } from "@testing-library/react";
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import showApplicationWindowInjectable from "../../main/start-main-application/lens-window/show-application-window.injectable";
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
describe("installing update using tray", () => {
let applicationBuilder: ApplicationBuilder;
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
let showApplicationWindowMock: jest.Mock;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
checkForPlatformUpdatesMock = asyncFn();
downloadPlatformUpdateMock = asyncFn();
showApplicationWindowMock = jest.fn();
mainDi.override(showApplicationWindowInjectable, () => showApplicationWindowMock);
mainDi.override(
checkForPlatformUpdatesInjectable,
() => checkForPlatformUpdatesMock,
);
mainDi.override(
downloadPlatformUpdateInjectable,
() => downloadPlatformUpdateMock,
);
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
mainDi.override(publishIsConfiguredInjectable, () => true);
});
});
describe("when started", () => {
let rendered: RenderResult;
beforeEach(async () => {
rendered = await applicationBuilder.render();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("user cannot install update yet", () => {
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
});
describe("when user checks for updates using tray", () => {
let processCheckingForUpdatesPromise: Promise<void>;
beforeEach(async () => {
processCheckingForUpdatesPromise =
applicationBuilder.tray.click("check-for-updates");
});
it("does not show application window yet", () => {
expect(showApplicationWindowMock).not.toHaveBeenCalled();
});
it("user cannot check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
).toBe(false);
});
it("name of tray item for checking updates indicates that checking is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
).toBe("Checking for updates...");
});
it("user cannot install update yet", () => {
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when no new update is discovered", () => {
beforeEach(async () => {
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: false,
});
await processCheckingForUpdatesPromise;
});
it("shows application window", () => {
expect(showApplicationWindowMock).toHaveBeenCalled();
});
it("user cannot install update", () => {
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
});
it("user can check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
).toBe(true);
});
it("name of tray item for checking updates no longer indicates that checking is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
).toBe("Check for updates");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when new update is discovered", () => {
beforeEach(async () => {
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-version",
});
await processCheckingForUpdatesPromise;
});
it("shows application window", () => {
expect(showApplicationWindowMock).toHaveBeenCalled();
});
it("user cannot check for updates again yet", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
).toBe(false);
});
it("name of tray item for checking updates indicates that downloading is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
).toBe("Downloading update some-version (0%)...");
});
it("when download progresses with decimals, percentage increases as integers", () => {
const progressOfUpdateDownload = applicationBuilder.dis.mainDi.inject(
progressOfUpdateDownloadInjectable,
);
progressOfUpdateDownload.set({ percentage: 42.424242 });
expect(
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
).toBe("Downloading update some-version (42%)...");
});
it("user still cannot install update", () => {
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when download fails", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: false });
});
it("user cannot install update", () => {
expect(
applicationBuilder.tray.get("install-update"),
).toBeUndefined();
});
it("user can check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
).toBe(true);
});
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
).toBe("Check for updates");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when download succeeds", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
});
it("user can install update", () => {
expect(
applicationBuilder.tray.get("install-update")?.label?.get(),
).toBe("Install update some-version");
});
it("user can check for updates again", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
).toBe(true);
});
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
expect(
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
).toBe("Check for updates");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
});
});
});
});

View File

@ -0,0 +1,225 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import quitAndInstallUpdateInjectable from "../../main/electron-app/features/quit-and-install-update.injectable";
import type { RenderResult } from "@testing-library/react";
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable";
import type { AskBoolean } from "../../main/ask-boolean/ask-boolean.injectable";
import askBooleanInjectable from "../../main/ask-boolean/ask-boolean.injectable";
import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable";
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
describe("installing update", () => {
let applicationBuilder: ApplicationBuilder;
let quitAndInstallUpdateMock: jest.Mock;
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
let setUpdateOnQuitMock: jest.Mock;
let showInfoNotificationMock: jest.Mock;
let askBooleanMock: AsyncFnMock<AskBoolean>;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
quitAndInstallUpdateMock = jest.fn();
checkForPlatformUpdatesMock = asyncFn();
downloadPlatformUpdateMock = asyncFn();
setUpdateOnQuitMock = jest.fn();
showInfoNotificationMock = jest.fn(() => () => {});
askBooleanMock = asyncFn();
rendererDi.override(showInfoNotificationInjectable, () => showInfoNotificationMock);
mainDi.override(askBooleanInjectable, () => askBooleanMock);
mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock);
mainDi.override(
checkForPlatformUpdatesInjectable,
() => checkForPlatformUpdatesMock,
);
mainDi.override(
downloadPlatformUpdateInjectable,
() => downloadPlatformUpdateMock,
);
mainDi.override(
quitAndInstallUpdateInjectable,
() => quitAndInstallUpdateMock,
);
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
mainDi.override(publishIsConfiguredInjectable, () => true);
});
});
describe("when started", () => {
let rendered: RenderResult;
let processCheckingForUpdates: () => Promise<void>;
beforeEach(async () => {
rendered = await applicationBuilder.render();
processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when user checks for updates", () => {
let processCheckingForUpdatesPromise: Promise<void>;
beforeEach(async () => {
processCheckingForUpdatesPromise = processCheckingForUpdates();
});
it("checks for updates", () => {
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
expect.any(Object),
{ allowDowngrade: true },
);
});
it("notifies the user that checking for updates is happening", () => {
expect(showInfoNotificationMock).toHaveBeenCalledWith("Checking for updates...");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when no new update is discovered", () => {
beforeEach(async () => {
showInfoNotificationMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: false,
});
await processCheckingForUpdatesPromise;
});
it("notifies the user", () => {
expect(showInfoNotificationMock).toHaveBeenCalledWith("No new updates available");
});
it("does not start downloading update", () => {
expect(downloadPlatformUpdateMock).not.toHaveBeenCalled();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when new update is discovered", () => {
beforeEach(async () => {
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-version",
});
await processCheckingForUpdatesPromise;
});
it("starts downloading the update", () => {
expect(downloadPlatformUpdateMock).toHaveBeenCalled();
});
it("notifies the user that download is happening", () => {
expect(showInfoNotificationMock).toHaveBeenCalledWith("Download for version some-version started...");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe("when download fails", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: false });
});
it("does not quit and install update yet", () => {
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
});
it("notifies the user about failed download", () => {
expect(showInfoNotificationMock).toHaveBeenCalledWith("Download of update failed");
});
it("does not ask user to install update", () => {
expect(askBooleanMock).not.toHaveBeenCalled();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
});
describe("when download succeeds", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
});
it("does not quit and install update yet", () => {
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("asks user to install update immediately", () => {
expect(askBooleanMock).toHaveBeenCalledWith({
title: "Update Available",
question:
"Version some-version of Lens IDE is available and ready to be installed. Would you like to update now?\n\n" +
"Lens should restart automatically, if it doesn't please restart manually. Installed extensions might require updating.",
});
});
describe("when user answers to install the update", () => {
beforeEach(async () => {
await askBooleanMock.resolve(true);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("quits application and installs the update", () => {
expect(quitAndInstallUpdateMock).toHaveBeenCalled();
});
});
describe("when user answers not to install the update", () => {
beforeEach(async () => {
await askBooleanMock.resolve(false);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("does not quit application and install the update", () => {
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
});
});
});
});
});
});
});

View File

@ -0,0 +1,117 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import type { RenderResult } from "@testing-library/react";
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
import periodicalCheckForUpdatesInjectable from "../../main/application-update/periodical-check-for-updates/periodical-check-for-updates.injectable";
const ENOUGH_TIME = 1000 * 60 * 60 * 2;
describe("periodical checking of updates", () => {
let applicationBuilder: ApplicationBuilder;
let processCheckingForUpdatesMock: AsyncFnMock<() => Promise<void>>;
beforeEach(() => {
jest.useFakeTimers();
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.unoverride(periodicalCheckForUpdatesInjectable);
mainDi.permitSideEffects(periodicalCheckForUpdatesInjectable);
processCheckingForUpdatesMock = asyncFn();
mainDi.override(
processCheckingForUpdatesInjectable,
() => processCheckingForUpdatesMock,
);
});
});
describe("given updater is enabled and configuration exists, when started", () => {
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
mainDi.override(publishIsConfiguredInjectable, () => true);
});
rendered = await applicationBuilder.render();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("checks for updates", () => {
expect(processCheckingForUpdatesMock).toHaveBeenCalled();
});
it("when just not enough time passes, does not check for updates again automatically yet", () => {
processCheckingForUpdatesMock.mockClear();
jest.advanceTimersByTime(ENOUGH_TIME - 1);
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
});
it("when just enough time passes, checks for updates again automatically", () => {
processCheckingForUpdatesMock.mockClear();
jest.advanceTimersByTime(ENOUGH_TIME);
expect(processCheckingForUpdatesMock).toHaveBeenCalled();
});
});
describe("given updater is enabled but no configuration exist, when started", () => {
beforeEach(async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
mainDi.override(publishIsConfiguredInjectable, () => false);
});
await applicationBuilder.render();
});
it("does not check for updates", () => {
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
});
it("when time passes, never checks for updates", () => {
jest.runOnlyPendingTimers();
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
});
});
describe("given updater is not enabled but and configuration exist, when started", () => {
beforeEach(async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(electronUpdaterIsActiveInjectable, () => false);
mainDi.override(publishIsConfiguredInjectable, () => true);
});
await applicationBuilder.render();
});
it("does not check for updates", () => {
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
});
it("when time passes, never checks for updates", () => {
jest.runOnlyPendingTimers();
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,331 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import quitAndInstallUpdateInjectable from "../../main/electron-app/features/quit-and-install-update.injectable";
import type { RenderResult } from "@testing-library/react";
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import type { UpdateChannel, UpdateChannelId } from "../../common/application-update/update-channels";
import { updateChannels } from "../../common/application-update/update-channels";
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
import selectedUpdateChannelInjectable from "../../common/application-update/selected-update-channel/selected-update-channel.injectable";
import type { IComputedValue } from "mobx";
import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable";
import type { AskBoolean } from "../../main/ask-boolean/ask-boolean.injectable";
import askBooleanInjectable from "../../main/ask-boolean/ask-boolean.injectable";
import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable";
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable";
describe("selection of update stability", () => {
let applicationBuilder: ApplicationBuilder;
let quitAndInstallUpdateMock: jest.Mock;
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
let setUpdateOnQuitMock: jest.Mock;
let showInfoNotificationMock: jest.Mock;
let askBooleanMock: AsyncFnMock<AskBoolean>;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
quitAndInstallUpdateMock = jest.fn();
checkForPlatformUpdatesMock = asyncFn();
downloadPlatformUpdateMock = asyncFn();
setUpdateOnQuitMock = jest.fn();
showInfoNotificationMock = jest.fn(() => () => {});
askBooleanMock = asyncFn();
rendererDi.override(showInfoNotificationInjectable, () => showInfoNotificationMock);
mainDi.override(askBooleanInjectable, () => askBooleanMock);
mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock);
mainDi.override(
checkForPlatformUpdatesInjectable,
() => checkForPlatformUpdatesMock,
);
mainDi.override(
downloadPlatformUpdateInjectable,
() => downloadPlatformUpdateMock,
);
mainDi.override(
quitAndInstallUpdateInjectable,
() => quitAndInstallUpdateMock,
);
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
mainDi.override(publishIsConfiguredInjectable, () => true);
});
});
describe("when started", () => {
let rendered: RenderResult;
let processCheckingForUpdates: () => Promise<void>;
beforeEach(async () => {
rendered = await applicationBuilder.render();
processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
describe('given update channel "alpha" is selected, when checking for updates', () => {
let selectedUpdateChannel: {
value: IComputedValue<UpdateChannel>;
setValue: (channelId: UpdateChannelId) => void;
};
beforeEach(() => {
selectedUpdateChannel = applicationBuilder.dis.mainDi.inject(
selectedUpdateChannelInjectable,
);
selectedUpdateChannel.setValue(updateChannels.alpha.id);
processCheckingForUpdates();
});
it('checks updates from update channel "alpha"', () => {
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
updateChannels.alpha,
{ allowDowngrade: true },
);
});
it("when update is discovered, does not check update from other update channels", async () => {
checkForPlatformUpdatesMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-version",
});
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
});
describe("when no update is discovered", () => {
beforeEach(async () => {
checkForPlatformUpdatesMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: false,
});
});
it('checks updates from update channel "beta"', () => {
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
updateChannels.beta,
{ allowDowngrade: true },
);
});
it("when update is discovered, does not check update from other update channels", async () => {
checkForPlatformUpdatesMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-version",
});
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
});
describe("when no update is discovered again", () => {
beforeEach(async () => {
checkForPlatformUpdatesMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: false,
});
});
it('finally checks updates from update channel "latest"', () => {
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
updateChannels.latest,
{ allowDowngrade: true },
);
});
it("when update is discovered, does not check update from other update channels", async () => {
checkForPlatformUpdatesMock.mockClear();
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-version",
});
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
});
});
});
});
describe('given update channel "beta" is selected', () => {
let selectedUpdateChannel: {
value: IComputedValue<UpdateChannel>;
setValue: (channelId: UpdateChannelId) => void;
};
beforeEach(() => {
selectedUpdateChannel = applicationBuilder.dis.mainDi.inject(
selectedUpdateChannelInjectable,
);
selectedUpdateChannel.setValue(updateChannels.beta.id);
});
describe("when checking for updates", () => {
beforeEach(() => {
processCheckingForUpdates();
});
describe('when update from "beta" channel is discovered', () => {
beforeEach(async () => {
await checkForPlatformUpdatesMock.resolve({
updateWasDiscovered: true,
version: "some-beta-version",
});
});
describe("when update is downloaded", () => {
beforeEach(async () => {
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
});
it("when user would close the application, installs the update", () => {
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(true);
});
it('given user changes update channel to "latest", when user would close the application, does not install the update for not being stable enough', () => {
selectedUpdateChannel.setValue(updateChannels.latest.id);
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(false);
});
it('given user changes update channel to "alpha", when user would close the application, installs the update for being stable enough', () => {
selectedUpdateChannel.setValue(updateChannels.alpha.id);
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(false);
});
});
});
});
});
});
it("given valid update channel selection is stored, when checking for updates, checks for updates from the update channel", async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
// TODO: Switch to more natural way of setting initial value
// TODO: UserStore is currently responsible for getting and setting initial value
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
selectedUpdateChannel.setValue(updateChannels.beta.id);
});
await applicationBuilder.render();
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.beta, expect.any(Object));
});
it("given invalid update channel selection is stored, when checking for updates, checks for updates from the update channel", async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
// TODO: Switch to more natural way of setting initial value
// TODO: UserStore is currently responsible for getting and setting initial value
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
selectedUpdateChannel.setValue("something-invalid" as UpdateChannelId);
});
await applicationBuilder.render();
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.latest, expect.any(Object));
});
it('given no update channel selection is stored and currently using stable release, when user checks for updates, checks for updates from "latest" update channel by default', async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(appVersionInjectable, () => "1.0.0");
});
await applicationBuilder.render();
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
updateChannels.latest,
{ allowDowngrade: true },
);
});
it('given no update channel selection is stored and currently using alpha release, when checking for updates, checks for updates from "alpha" channel', async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(appVersionInjectable, () => "1.0.0-alpha");
});
await applicationBuilder.render();
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.alpha, expect.any(Object));
});
it('given no update channel selection is stored and currently using beta release, when checking for updates, checks for updates from "beta" channel', async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(appVersionInjectable, () => "1.0.0-beta");
});
await applicationBuilder.render();
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.beta, expect.any(Object));
});
it("given update channel selection is stored and currently using prerelease, when checking for updates, checks for updates from stored channel", async () => {
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
mainDi.override(appVersionInjectable, () => "1.0.0-alpha");
// TODO: Switch to more natural way of setting initial value
// TODO: UserStore is currently responsible for getting and setting initial value
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
selectedUpdateChannel.setValue(updateChannels.beta.id);
});
await applicationBuilder.render();
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
processCheckingForUpdates();
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.beta, expect.any(Object));
});
});

View File

@ -328,6 +328,9 @@ exports[`cluster - order of sidebar items when rendered renders 1`] = `
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -723,5 +726,8 @@ exports[`cluster - order of sidebar items when rendered when parent is expanded
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -293,6 +293,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -589,6 +592,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -909,6 +915,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1234,6 +1243,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
<div
data-testid="some-child-page"
/>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1534,6 +1546,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
<div
data-testid="some-child-page"
/>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1854,6 +1869,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -2150,5 +2168,8 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -261,14 +261,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="false"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -293,6 +293,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -557,14 +560,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="false"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -589,6 +592,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -853,14 +859,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="false"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -887,15 +893,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-child-id"
data-id-test="some-extension-name-some-child-id"
data-is-active-test="false"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-child-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-some-extension-id-some-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-child-id"
href="/"
>
<span
@ -907,15 +913,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-other-child-id"
data-id-test="some-extension-name-some-other-child-id"
data-is-active-test="false"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-other-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-other-child-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-some-extension-id-some-other-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-other-child-id"
href="/"
>
<span
@ -929,6 +935,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1193,15 +1202,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="true"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
aria-current="page"
class="nav-item flex gaps align-center expandable active"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -1228,16 +1237,16 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-child-id"
data-id-test="some-extension-name-some-child-id"
data-is-active-test="true"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-child-id"
data-testid="sidebar-item"
>
<a
aria-current="page"
class="nav-item flex gaps align-center active"
data-testid="sidebar-item-link-for-some-extension-id-some-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-child-id"
href="/"
>
<span
@ -1249,15 +1258,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-other-child-id"
data-id-test="some-extension-name-some-other-child-id"
data-is-active-test="false"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-other-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-other-child-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-some-extension-id-some-other-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-other-child-id"
href="/"
>
<span
@ -1281,7 +1290,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
<div
class="Tab flex gaps align-center active"
data-is-active-test="true"
data-testid="tab-link-for-some-extension-id-some-child-id"
data-testid="tab-link-for-some-extension-name-some-child-id"
role="tab"
tabindex="0"
>
@ -1294,7 +1303,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
<div
class="Tab flex gaps align-center"
data-is-active-test="false"
data-testid="tab-link-for-some-extension-id-some-other-child-id"
data-testid="tab-link-for-some-extension-name-some-other-child-id"
role="tab"
tabindex="0"
>
@ -1313,6 +1322,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</main>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1577,15 +1589,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="true"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
aria-current="page"
class="nav-item flex gaps align-center expandable active"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -1612,15 +1624,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-child-id"
data-id-test="some-extension-name-some-child-id"
data-is-active-test="false"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-child-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-some-extension-id-some-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-child-id"
href="/"
>
<span
@ -1632,16 +1644,16 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-other-child-id"
data-id-test="some-extension-name-some-other-child-id"
data-is-active-test="true"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-other-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-other-child-id"
data-testid="sidebar-item"
>
<a
aria-current="page"
class="nav-item flex gaps align-center active"
data-testid="sidebar-item-link-for-some-extension-id-some-other-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-other-child-id"
href="/"
>
<span
@ -1665,7 +1677,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
<div
class="Tab flex gaps align-center"
data-is-active-test="false"
data-testid="tab-link-for-some-extension-id-some-child-id"
data-testid="tab-link-for-some-extension-name-some-child-id"
role="tab"
tabindex="0"
>
@ -1678,7 +1690,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
<div
class="Tab flex gaps align-center active"
data-is-active-test="true"
data-testid="tab-link-for-some-extension-id-some-other-child-id"
data-testid="tab-link-for-some-extension-name-some-other-child-id"
role="tab"
tabindex="0"
>
@ -1697,6 +1709,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</main>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1961,15 +1976,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="true"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
aria-current="page"
class="nav-item flex gaps align-center expandable active"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -2004,7 +2019,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
<div
class="Tab flex gaps align-center active"
data-is-active-test="true"
data-testid="tab-link-for-some-extension-id-some-child-id"
data-testid="tab-link-for-some-extension-name-some-child-id"
role="tab"
tabindex="0"
>
@ -2017,7 +2032,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
<div
class="Tab flex gaps align-center"
data-is-active-test="false"
data-testid="tab-link-for-some-extension-id-some-other-child-id"
data-testid="tab-link-for-some-extension-name-some-other-child-id"
role="tab"
tabindex="0"
>
@ -2036,6 +2051,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</main>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -2300,14 +2318,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="false"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -2334,15 +2352,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-child-id"
data-id-test="some-extension-name-some-child-id"
data-is-active-test="false"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-child-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-some-extension-id-some-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-child-id"
href="/"
>
<span
@ -2354,15 +2372,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-other-child-id"
data-id-test="some-extension-name-some-other-child-id"
data-is-active-test="false"
data-parent-id-test="some-extension-id-some-parent-id"
data-test-id="some-extension-id-some-other-child-id"
data-parent-id-test="some-extension-name-some-parent-id"
data-test-id="some-extension-name-some-other-child-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-some-extension-id-some-other-child-id"
data-testid="sidebar-item-link-for-some-extension-name-some-other-child-id"
href="/"
>
<span
@ -2376,6 +2394,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -2640,14 +2661,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
<div
class="SidebarItem"
data-id-test="some-extension-id-some-parent-id"
data-id-test="some-extension-name-some-parent-id"
data-is-active-test="false"
data-test-id="some-extension-id-some-parent-id"
data-test-id="some-extension-name-some-parent-id"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-some-extension-id-some-parent-id"
data-testid="sidebar-item-link-for-some-extension-name-some-parent-id"
href="/"
>
<div>
@ -2672,5 +2693,8 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -261,6 +261,9 @@ exports[`cluster - visibility of sidebar items given kube resource for route is
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -573,5 +576,8 @@ exports[`cluster - visibility of sidebar items given kube resource for route is
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -19,7 +19,7 @@ describe("cluster - order of sidebar items", () => {
beforeEach(() => {
applicationBuilder = getApplicationBuilder().setEnvironmentToClusterFrame();
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.register(testSidebarItemsInjectable);
});
});

View File

@ -21,6 +21,8 @@ import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable"
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
import { getSidebarItem } from "../utils";
import sidebarStorageInjectable from "../../renderer/components/layout/sidebar-storage/sidebar-storage.injectable";
describe("cluster - sidebar and tab navigation for core", () => {
let applicationBuilder: ApplicationBuilder;
@ -35,7 +37,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
applicationBuilder.setEnvironmentToClusterFrame();
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(
directoryForLensLocalStorageInjectable,
() => "/some-directory-for-lens-local-storage",
@ -45,7 +47,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
describe("given core registrations", () => {
beforeEach(() => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.register(testRouteInjectable);
rendererDi.register(testRouteComponentInjectable);
rendererDi.register(testSidebarItemsInjectable);
@ -72,13 +74,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent is highlighted", () => {
const parent = getSidebarItem(rendered, "some-parent-id");
expect(parent.dataset.isActiveTest).toBe("true");
expect(parent?.dataset.isActiveTest).toBe("true");
});
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child).toBe(null);
expect(child).toBeUndefined();
});
it("child page is shown", () => {
@ -102,6 +104,12 @@ describe("cluster - sidebar and tab navigation for core", () => {
);
});
applicationBuilder.beforeRender(async ({ rendererDi }) => {
const sidebarStorage = rendererDi.inject(sidebarStorageInjectable);
await sidebarStorage.whenReady;
});
rendered = await applicationBuilder.render();
});
@ -112,13 +120,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent sidebar item is not highlighted", () => {
const parent = getSidebarItem(rendered, "some-parent-id");
expect(parent.dataset.isActiveTest).toBe("false");
expect(parent?.dataset.isActiveTest).toBe("false");
});
it("parent sidebar item is expanded", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child).not.toBe(null);
expect(child).not.toBeUndefined();
});
});
@ -148,7 +156,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child).toBe(null);
expect(child).toBeUndefined();
});
});
@ -175,7 +183,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child).toBe(null);
expect(child).toBeUndefined();
});
});
@ -191,13 +199,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent sidebar item is not highlighted", () => {
const parent = getSidebarItem(rendered, "some-parent-id");
expect(parent.dataset.isActiveTest).toBe("false");
expect(parent?.dataset.isActiveTest).toBe("false");
});
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child).toBe(null);
expect(child).toBeUndefined();
});
describe("when a parent sidebar item is expanded", () => {
@ -216,13 +224,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent sidebar item is not highlighted", () => {
const parent = getSidebarItem(rendered, "some-parent-id");
expect(parent.dataset.isActiveTest).toBe("false");
expect(parent?.dataset.isActiveTest).toBe("false");
});
it("parent sidebar item is expanded", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child).not.toBe(null);
expect(child).not.toBeUndefined();
});
describe("when a child of the parent is selected", () => {
@ -241,13 +249,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
it("parent is highlighted", () => {
const parent = getSidebarItem(rendered, "some-parent-id");
expect(parent.dataset.isActiveTest).toBe("true");
expect(parent?.dataset.isActiveTest).toBe("true");
});
it("child is highlighted", () => {
const child = getSidebarItem(rendered, "some-child-id");
expect(child.dataset.isActiveTest).toBe("true");
expect(child?.dataset.isActiveTest).toBe("true");
});
it("child page is shown", () => {
@ -288,11 +296,6 @@ describe("cluster - sidebar and tab navigation for core", () => {
});
});
const getSidebarItem = (rendered: RenderResult, itemId: string) =>
rendered
.queryAllByTestId("sidebar-item")
.find((x) => x.dataset.idTest === itemId) || null;
const testSidebarItemsInjectable = getInjectable({
id: "some-sidebar-items-injectable",

View File

@ -5,8 +5,6 @@
import React from "react";
import type { RenderResult } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
import directoryForLensLocalStorageInjectable from "../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import routesInjectable from "../../renderer/routes/routes.injectable";
import { matches } from "lodash/fp";
@ -17,6 +15,10 @@ import pathExistsInjectable from "../../common/fs/path-exists.injectable";
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
import assert from "assert";
import { getSidebarItem } from "../utils";
import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
describe("cluster - sidebar and tab navigation for extensions", () => {
let applicationBuilder: ApplicationBuilder;
@ -31,7 +33,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
applicationBuilder.setEnvironmentToClusterFrame();
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(
directoryForLensLocalStorageInjectable,
() => "/some-directory-for-lens-local-storage",
@ -41,9 +43,8 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
describe("given extension with cluster pages and cluster page menus", () => {
beforeEach(async () => {
const testExtension = getRendererExtensionFake(
extensionStubWithSidebarItems,
);
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const testExtension = getRendererExtensionFake(extensionStubWithSidebarItems);
await applicationBuilder.addExtensions(testExtension);
});
@ -51,19 +52,17 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
describe("given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered", () => {
beforeEach(async () => {
applicationBuilder.beforeRender(({ rendererDi }) => {
const navigateToRoute = rendererDi.inject(
navigateToRouteInjectionToken,
);
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
const route = rendererDi
.inject(routesInjectable)
.get()
.find(
matches({
path: "/extension/some-extension-id/some-child-page-id",
path: "/extension/some-extension-name/some-child-page-id",
}),
);
assert(route);
navigateToRoute(route);
});
@ -77,19 +76,19 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent is highlighted", () => {
const parent = getSidebarItem(
rendered,
"some-extension-id-some-parent-id",
"some-extension-name-some-parent-id",
);
expect(parent.dataset.isActiveTest).toBe("true");
expect(parent?.dataset.isActiveTest).toBe("true");
});
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child).toBe(null);
expect(child).toBeUndefined();
});
it("child page is shown", () => {
@ -106,7 +105,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
"/some-directory-for-lens-local-storage/app.json",
{
sidebar: {
expanded: { "some-extension-id-some-parent-id": true },
expanded: { "some-extension-name-some-parent-id": true },
width: 200,
},
},
@ -123,19 +122,19 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent sidebar item is not highlighted", () => {
const parent = getSidebarItem(
rendered,
"some-extension-id-some-parent-id",
"some-extension-name-some-parent-id",
);
expect(parent.dataset.isActiveTest).toBe("false");
expect(parent?.dataset.isActiveTest).toBe("false");
});
it("parent sidebar item is expanded", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child).not.toBe(null);
expect(child).not.toBeUndefined();
});
});
@ -148,7 +147,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
"/some-directory-for-lens-local-storage/app.json",
{
sidebar: {
expanded: { "some-extension-id-some-unknown-parent-id": true },
expanded: { "some-extension-name-some-unknown-parent-id": true },
width: 200,
},
},
@ -165,10 +164,10 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child).toBe(null);
expect(child).toBeUndefined();
});
});
@ -195,10 +194,10 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child).toBe(null);
expect(child).toBeUndefined();
});
});
@ -214,25 +213,25 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent sidebar item is not highlighted", () => {
const parent = getSidebarItem(
rendered,
"some-extension-id-some-parent-id",
"some-extension-name-some-parent-id",
);
expect(parent.dataset.isActiveTest).toBe("false");
expect(parent?.dataset.isActiveTest).toBe("false");
});
it("parent sidebar item is not expanded", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child).toBe(null);
expect(child).toBeUndefined();
});
describe("when a parent sidebar item is expanded", () => {
beforeEach(() => {
const parentLink = rendered.getByTestId(
"sidebar-item-link-for-some-extension-id-some-parent-id",
"sidebar-item-link-for-some-extension-name-some-parent-id",
);
fireEvent.click(parentLink);
@ -245,25 +244,25 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent sidebar item is not highlighted", () => {
const parent = getSidebarItem(
rendered,
"some-extension-id-some-parent-id",
"some-extension-name-some-parent-id",
);
expect(parent.dataset.isActiveTest).toBe("false");
expect(parent?.dataset.isActiveTest).toBe("false");
});
it("parent sidebar item is expanded", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child).not.toBe(null);
expect(child).not.toBeUndefined();
});
describe("when a child of the parent is selected", () => {
beforeEach(() => {
const childLink = rendered.getByTestId(
"sidebar-item-link-for-some-extension-id-some-child-id",
"sidebar-item-link-for-some-extension-name-some-child-id",
);
fireEvent.click(childLink);
@ -276,19 +275,19 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("parent is highlighted", () => {
const parent = getSidebarItem(
rendered,
"some-extension-id-some-parent-id",
"some-extension-name-some-parent-id",
);
expect(parent.dataset.isActiveTest).toBe("true");
expect(parent?.dataset.isActiveTest).toBe("true");
});
it("child is highlighted", () => {
const child = getSidebarItem(
rendered,
"some-extension-id-some-child-id",
"some-extension-name-some-child-id",
);
expect(child.dataset.isActiveTest).toBe("true");
expect(child?.dataset.isActiveTest).toBe("true");
});
it("child page is shown", () => {
@ -301,7 +300,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("tab for child page is active", () => {
const tabLink = rendered.getByTestId(
"tab-link-for-some-extension-id-some-child-id",
"tab-link-for-some-extension-name-some-child-id",
);
expect(tabLink.dataset.isActiveTest).toBe("true");
@ -309,7 +308,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("tab for sibling page is not active", () => {
const tabLink = rendered.getByTestId(
"tab-link-for-some-extension-id-some-other-child-id",
"tab-link-for-some-extension-name-some-other-child-id",
);
expect(tabLink.dataset.isActiveTest).toBe("false");
@ -338,7 +337,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
expect(actual).toEqual({
sidebar: {
expanded: { "some-extension-id-some-parent-id": true },
expanded: { "some-extension-name-some-parent-id": true },
width: 200,
},
});
@ -347,7 +346,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
describe("when selecting sibling tab", () => {
beforeEach(() => {
const childTabLink = rendered.getByTestId(
"tab-link-for-some-extension-id-some-other-child-id",
"tab-link-for-some-extension-name-some-other-child-id",
);
fireEvent.click(childTabLink);
@ -365,7 +364,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("tab for sibling page is active", () => {
const tabLink = rendered.getByTestId(
"tab-link-for-some-extension-id-some-other-child-id",
"tab-link-for-some-extension-name-some-other-child-id",
);
expect(tabLink.dataset.isActiveTest).toBe("true");
@ -373,7 +372,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
it("tab for previous page is not active", () => {
const tabLink = rendered.getByTestId(
"tab-link-for-some-extension-id-some-child-id",
"tab-link-for-some-extension-name-some-child-id",
);
expect(tabLink.dataset.isActiveTest).toBe("false");
@ -385,9 +384,9 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
});
});
const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
const extensionStubWithSidebarItems: FakeExtensionData = {
id: "some-extension-id",
name: "some-extension-name",
clusterPages: [
{
components: {
@ -396,7 +395,6 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
},
},
},
{
id: "some-child-page-id",
@ -404,7 +402,6 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
Page: () => <div data-testid="some-child-page">Some child page</div>,
},
},
{
id: "some-other-child-page-id",
@ -415,7 +412,6 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
},
},
],
clusterPageMenus: [
{
id: "some-parent-id",
@ -433,7 +429,7 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
title: "Child 1",
components: {
Icon: null,
Icon: null as never,
},
},
@ -444,13 +440,8 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
title: "Child 2",
components: {
Icon: null,
Icon: null as never,
},
},
],
};
const getSidebarItem = (rendered: RenderResult, itemId: string) =>
rendered
.queryAllByTestId("sidebar-item")
.find((x) => x.dataset.idTest === itemId) || null;

View File

@ -14,6 +14,7 @@ import { routeInjectionToken } from "../../common/front-end-routing/route-inject
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
import { getSidebarItem } from "../utils";
describe("cluster - visibility of sidebar items", () => {
let applicationBuilder: ApplicationBuilder;
@ -24,7 +25,7 @@ describe("cluster - visibility of sidebar items", () => {
applicationBuilder.setEnvironmentToClusterFrame();
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.register(testRouteInjectable);
rendererDi.register(testRouteComponentInjectable);
rendererDi.register(testSidebarItemsInjectable);
@ -43,7 +44,7 @@ describe("cluster - visibility of sidebar items", () => {
it("related sidebar item does not exist", () => {
const item = getSidebarItem(rendered, "some-item-id");
expect(item).toBeNull();
expect(item).toBeUndefined();
});
describe("when kube resource becomes allowed", () => {
@ -58,17 +59,12 @@ describe("cluster - visibility of sidebar items", () => {
it("related sidebar item exists", () => {
const item = getSidebarItem(rendered, "some-item-id");
expect(item).not.toBeNull();
expect(item).not.toBeUndefined();
});
});
});
});
const getSidebarItem = (rendered: RenderResult, itemId: string) =>
rendered
.queryAllByTestId("sidebar-item")
.find((x) => x.dataset.idTest === itemId) || null;
const testRouteInjectable = getInjectable({
id: "some-route-injectable-id",

View File

@ -2,12 +2,11 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { TestExtension } from "../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFake } from "../renderer/components/test-utils/get-renderer-extension-fake";
import type { FakeExtensionData, TestExtension } from "../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../renderer/components/test-utils/get-renderer-extension-fake";
import React from "react";
import type { RenderResult } from "@testing-library/react";
import currentPathInjectable from "../renderer/routes/current-path.injectable";
import type { LensRendererExtension } from "../extensions/lens-renderer-extension";
import type { ApplicationBuilder } from "../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../renderer/components/test-utils/get-application-builder";
@ -18,6 +17,7 @@ describe("extension special characters in page registrations", () => {
beforeEach(async () => {
applicationBuilder = getApplicationBuilder();
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
testExtension = getRendererExtensionFake(
extensionWithPagesHavingSpecialCharacters,
@ -44,14 +44,14 @@ describe("extension special characters in page registrations", () => {
it("knows URL", () => {
const currentPath = applicationBuilder.dis.rendererDi.inject(currentPathInjectable);
expect(currentPath.get()).toBe("/extension/some-extension-id--/some-page-id");
expect(currentPath.get()).toBe("/extension/some-extension-name--/some-page-id");
});
});
});
const extensionWithPagesHavingSpecialCharacters: Partial<LensRendererExtension> = {
id: "@some-extension-id/",
const extensionWithPagesHavingSpecialCharacters: FakeExtensionData = {
id: "some-extension-id",
name: "@some-extension-name/",
globalPages: [
{
id: "/some-page-id/",

View File

@ -1,6 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`extensions - navigation using application menu renders 1`] = `<div />`;
exports[`extensions - navigation using application menu renders 1`] = `
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
exports[`extensions - navigation using application menu when navigating to extensions using application menu renders 1`] = `
<div>
@ -23,9 +29,7 @@ exports[`extensions - navigation using application menu when navigating to exten
class="notice mb-14 mt-3"
>
<p>
Add new features via Lens Extensions.
Check out
Add new features via Lens Extensions. Check out the
<a
href="https://docs.k8slens.dev/main//extensions/"
rel="noreferrer"
@ -33,8 +37,7 @@ exports[`extensions - navigation using application menu when navigating to exten
>
docs
</a>
and list of
and list of
<a
href="https://github.com/lensapp/lens-extensions/blob/main/README.md"
rel="noreferrer"
@ -121,5 +124,8 @@ exports[`extensions - navigation using application menu when navigating to exten
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -6,12 +6,7 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
import extensionsStoreInjectable from "../../extensions/extensions-store/extensions-store.injectable";
import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store";
import fileSystemProvisionerStoreInjectable from "../../extensions/extension-loader/create-extension-instance/file-system-provisioner-store/file-system-provisioner-store.injectable";
import type { FileSystemProvisionerStore } from "../../extensions/extension-loader/create-extension-instance/file-system-provisioner-store/file-system-provisioner-store";
import focusWindowInjectable from "../../renderer/ipc-channel-listeners/focus-window.injectable";
import focusWindowInjectable from "../../renderer/navigation/focus-window.injectable";
// TODO: Make components free of side effects by making them deterministic
jest.mock("../../renderer/components/input/input");
@ -22,11 +17,7 @@ describe("extensions - navigation using application menu", () => {
let focusWindowMock: jest.Mock;
beforeEach(async () => {
applicationBuilder = getApplicationBuilder().beforeSetups(({ mainDi, rendererDi }) => {
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
rendererDi.override(extensionsStoreInjectable, () => ({}) as unknown as ExtensionsStore);
rendererDi.override(fileSystemProvisionerStoreInjectable, () => ({}) as unknown as FileSystemProvisionerStore);
applicationBuilder = getApplicationBuilder().beforeApplicationStart(({ rendererDi }) => {
focusWindowMock = jest.fn();
rendererDi.override(focusWindowInjectable, () => focusWindowMock);
@ -46,8 +37,8 @@ describe("extensions - navigation using application menu", () => {
});
describe("when navigating to extensions using application menu", () => {
beforeEach(() => {
applicationBuilder.applicationMenu.click("root.extensions");
beforeEach(async () => {
await applicationBuilder.applicationMenu.click("root.extensions");
});
it("focuses the window", () => {

View File

@ -0,0 +1,461 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`helm-charts - navigation to Helm charts when navigating to Helm charts renders 1`] = `
<div>
<div
class="flex flex-col"
data-testid="cluster-sidebar"
>
<div
class="SidebarCluster"
>
<div
class="Avatar rounded loadingAvatar"
style="width: 40px; height: 40px;"
>
??
</div>
<div
class="loadingClusterName"
/>
</div>
<div
class="sidebarNav sidebar-active-status"
>
<div
class="SidebarItem"
data-id-test="workloads"
data-is-active-test="false"
data-test-id="workloads"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-workloads"
href="/"
>
<i
class="Icon svg focusable"
>
<span
class="icon"
/>
</i>
<span
class="link-text box grow"
>
Workloads
</span>
<i
class="Icon expand-icon box right material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
<div
class="SidebarItem"
data-id-test="config"
data-is-active-test="false"
data-test-id="config"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-config"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="list"
>
list
</span>
</i>
<span
class="link-text box grow"
>
Config
</span>
</a>
</div>
<div
class="SidebarItem"
data-id-test="network"
data-is-active-test="false"
data-test-id="network"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-network"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="device_hub"
>
device_hub
</span>
</i>
<span
class="link-text box grow"
>
Network
</span>
<i
class="Icon expand-icon box right material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
<div
class="SidebarItem"
data-id-test="storage"
data-is-active-test="false"
data-test-id="storage"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-storage"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="storage"
>
storage
</span>
</i>
<span
class="link-text box grow"
>
Storage
</span>
</a>
</div>
<div
class="SidebarItem"
data-id-test="helm"
data-is-active-test="true"
data-test-id="helm"
data-testid="sidebar-item"
>
<a
aria-current="page"
class="nav-item flex gaps align-center expandable active"
data-testid="sidebar-item-link-for-helm"
href="/"
>
<i
class="Icon svg focusable"
>
<span
class="icon"
/>
</i>
<span
class="link-text box grow"
>
Helm
</span>
<i
class="Icon expand-icon box right material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
<div
class="SidebarItem"
data-id-test="user-management"
data-is-active-test="false"
data-test-id="user-management"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center"
data-testid="sidebar-item-link-for-user-management"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="security"
>
security
</span>
</i>
<span
class="link-text box grow"
>
Access Control
</span>
</a>
</div>
<div
class="SidebarItem"
data-id-test="custom-resources"
data-is-active-test="false"
data-test-id="custom-resources"
data-testid="sidebar-item"
>
<a
class="nav-item flex gaps align-center expandable"
data-testid="sidebar-item-link-for-custom-resources"
href="/"
>
<i
class="Icon material focusable"
>
<span
class="icon"
data-icon-name="extension"
>
extension
</span>
</i>
<span
class="link-text box grow"
>
Custom Resources
</span>
<i
class="Icon expand-icon box right material focusable"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
</a>
</div>
</div>
</div>
<div
class="TabLayout"
data-testid="tab-layout"
>
<div
class="Tabs center scrollable"
>
<div
class="Tab flex gaps align-center active"
data-is-active-test="true"
data-testid="tab-link-for-charts"
role="tab"
tabindex="0"
>
<div
class="label"
>
Charts
</div>
</div>
<div
class="Tab flex gaps align-center"
data-is-active-test="false"
data-testid="tab-link-for-releases"
role="tab"
tabindex="0"
>
<div
class="label"
>
Releases
</div>
</div>
</div>
<main>
<div
data-testid="page-for-helm-charts"
style="display: none;"
/>
<div
class="ItemListLayout flex column HelmCharts"
>
<div
class="header flex gaps align-center"
>
<div
class="Input SearchInput focused"
>
<label
class="input-area flex gaps align-center"
id=""
>
<input
class="input box grow"
placeholder="Search Helm Charts..."
spellcheck="false"
value=""
/>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="search"
>
search
</span>
</i>
</label>
<div
class="input-info flex gaps"
/>
</div>
</div>
<div
class="items box grow flex column"
>
<div
class="Table flex column HelmCharts box grow dark selectable scrollable sortable autoSize virtual"
>
<div
class="TableHead sticky nowrap topLine"
>
<div
class="TableCell icon nowrap"
>
<div
class="content"
/>
</div>
<div
class="TableCell name nowrap sorting"
id="name"
>
<div
class="content"
>
Name
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell description nowrap"
id="description"
>
<div
class="content"
>
Description
</div>
</div>
<div
class="TableCell version nowrap"
id="version"
>
<div
class="content"
>
Version
</div>
</div>
<div
class="TableCell app-version nowrap"
id="app-version"
>
<div
class="content"
>
App Version
</div>
</div>
<div
class="TableCell repository nowrap sorting"
id="repo"
>
<div
class="content"
>
Repository
</div>
<i
class="Icon sortIcon material focusable"
>
<span
class="icon"
data-icon-name="arrow_drop_down"
>
arrow_drop_down
</span>
</i>
</div>
<div
class="TableCell menu nowrap"
>
<div
class="content"
>
<i
class="Icon material interactive focusable"
id="menu_actions_17"
tabindex="0"
>
<span
class="icon"
data-icon-name="more_vert"
>
more_vert
</span>
</i>
</div>
</div>
</div>
<div
class="Spinner singleColor center"
/>
</div>
<div
class="AddRemoveButtons flex gaps"
/>
</div>
</div>
</main>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
describe("helm-charts - navigation to Helm charts", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
});
describe("when navigating to Helm charts", () => {
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder.setEnvironmentToClusterFrame();
rendered = await applicationBuilder.render();
applicationBuilder.helmCharts.navigate();
});
it("renders", () => {
expect(rendered.container).toMatchSnapshot();
});
it("shows page for Helm charts", () => {
const page = rendered.getByTestId("page-for-helm-charts");
expect(page).not.toBeNull();
});
});
});

View File

@ -2,8 +2,8 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { TestExtension } from "../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFake } from "../renderer/components/test-utils/get-renderer-extension-fake";
import type { FakeExtensionData, TestExtension } from "../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../renderer/components/test-utils/get-renderer-extension-fake";
import React from "react";
import type { RenderResult } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
@ -11,7 +11,6 @@ import isEmpty from "lodash/isEmpty";
import queryParametersInjectable from "../renderer/routes/query-parameters.injectable";
import currentPathInjectable from "../renderer/routes/current-path.injectable";
import type { IComputedValue } from "mobx";
import type { LensRendererExtension } from "../extensions/lens-renderer-extension";
import { getApplicationBuilder } from "../renderer/components/test-utils/get-application-builder";
describe("navigate to extension page", () => {
@ -22,6 +21,7 @@ describe("navigate to extension page", () => {
beforeEach(async () => {
const applicationBuilder = getApplicationBuilder();
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
testExtension = getRendererExtensionFake(
extensionWithPagesHavingParameters,
@ -51,7 +51,7 @@ describe("navigate to extension page", () => {
});
it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id");
expect(currentPath.get()).toBe("/extension/some-extension-name");
});
it("query parameters is empty", () => {
@ -70,7 +70,7 @@ describe("navigate to extension page", () => {
});
it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id");
expect(currentPath.get()).toBe("/extension/some-extension-name");
});
it("knows query parameters", () => {
@ -98,7 +98,7 @@ describe("navigate to extension page", () => {
});
it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id");
expect(currentPath.get()).toBe("/extension/some-extension-name");
});
it("knows query parameters", () => {
@ -120,14 +120,14 @@ describe("navigate to extension page", () => {
});
it("URL is correct", () => {
expect(currentPath.get()).toBe("/extension/some-extension-id/some-child-page-id");
expect(currentPath.get()).toBe("/extension/some-extension-name/some-child-page-id");
});
});
});
const extensionWithPagesHavingParameters: Partial<LensRendererExtension> = {
const extensionWithPagesHavingParameters: FakeExtensionData = {
id: "some-extension-id",
name: "some-extension-name",
globalPages: [
{
components: {
@ -159,20 +159,14 @@ const extensionWithPagesHavingParameters: Partial<LensRendererExtension> = {
params: {
someStringParameter: "some-string-value",
someNumberParameter: {
defaultValue: 42,
stringify: (value) => value.toString(),
parse: (value) => (value ? Number(value) : undefined),
},
someArrayParameter: {
defaultValue: ["some-array-value", "some-other-array-value"],
stringify: (value) => value.join(","),
parse: (value: string[]) => (!isEmpty(value) ? value : undefined),
},
},

View File

@ -31,7 +31,7 @@ describe("navigating between routes", () => {
describe("given route without path parameters", () => {
beforeEach(async () => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.register(testRouteWithoutPathParametersInjectable);
rendererDi.register(testRouteWithoutPathParametersComponentInjectable);
});
@ -102,7 +102,7 @@ describe("navigating between routes", () => {
describe("given route with optional path parameters", () => {
beforeEach(async () => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.register(routeWithOptionalPathParametersInjectable);
rendererDi.register(routeWithOptionalPathParametersComponentInjectable);
});

View File

@ -124,7 +124,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-26-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -140,7 +140,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-26-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -150,7 +150,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<input
aria-autocomplete="list"
aria-describedby="react-select-26-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -209,7 +209,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-27-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -225,7 +225,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-27-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -235,7 +235,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<input
aria-autocomplete="list"
aria-describedby="react-select-27-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -281,17 +281,12 @@ exports[`preferences - closing-preferences given accessing preferences directly
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -349,7 +344,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-28-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -361,13 +356,12 @@ exports[`preferences - closing-preferences given accessing preferences directly
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-28-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -375,7 +369,6 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<input
aria-autocomplete="list"
aria-describedby="react-select-28-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -434,7 +427,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-29-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -450,7 +443,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-29-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -460,7 +453,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
>
<input
aria-autocomplete="list"
aria-describedby="react-select-29-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -542,6 +535,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -684,6 +680,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -692,6 +691,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
<div>
Some front page
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -700,6 +702,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
<div>
Some front page
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -827,7 +832,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -843,7 +848,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -853,7 +858,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -912,7 +917,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -928,7 +933,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -938,7 +943,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -984,17 +989,12 @@ exports[`preferences - closing-preferences given already in a page and then navi
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -1052,7 +1052,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -1064,13 +1064,12 @@ exports[`preferences - closing-preferences given already in a page and then navi
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -1078,7 +1077,6 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -1137,7 +1135,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -1153,7 +1151,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -1163,7 +1161,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -1245,6 +1243,9 @@ exports[`preferences - closing-preferences given already in a page and then navi
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1387,6 +1388,9 @@ exports[`preferences - closing-preferences given already in a page and then navi
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1529,6 +1533,9 @@ exports[`preferences - closing-preferences given already in a page and then navi
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1671,5 +1678,8 @@ exports[`preferences - closing-preferences given already in a page and then navi
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -199,6 +199,9 @@ exports[`preferences - navigation to application preferences given in some child
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -314,7 +317,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -330,7 +333,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -340,7 +343,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -399,7 +402,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -415,7 +418,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -425,7 +428,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -471,17 +474,12 @@ exports[`preferences - navigation to application preferences given in some child
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -539,7 +537,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -551,13 +549,12 @@ exports[`preferences - navigation to application preferences given in some child
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -565,7 +562,6 @@ exports[`preferences - navigation to application preferences given in some child
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -624,7 +620,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -640,7 +636,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -650,7 +646,7 @@ exports[`preferences - navigation to application preferences given in some child
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -732,5 +728,8 @@ exports[`preferences - navigation to application preferences given in some child
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -112,7 +112,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -128,7 +128,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -138,7 +138,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -197,7 +197,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -213,7 +213,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -223,7 +223,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -269,17 +269,12 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -337,7 +332,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -349,13 +344,12 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -363,7 +357,6 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -422,7 +415,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -438,7 +431,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -448,7 +441,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -530,6 +523,9 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -668,7 +664,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-14-live-region"
id="react-select-minimap-input-live-region"
/>
<span
aria-atomic="false"
@ -684,7 +680,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-14-placeholder"
id="react-select-minimap-input-placeholder"
>
Select...
</div>
@ -694,7 +690,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<input
aria-autocomplete="list"
aria-describedby="react-select-14-placeholder"
aria-describedby="react-select-minimap-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -752,7 +748,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-15-live-region"
id="react-select-editor-line-numbers-input-live-region"
/>
<span
aria-atomic="false"
@ -768,7 +764,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-15-placeholder"
id="react-select-editor-line-numbers-input-placeholder"
>
Select...
</div>
@ -778,7 +774,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
>
<input
aria-autocomplete="list"
aria-describedby="react-select-15-placeholder"
aria-describedby="react-select-editor-line-numbers-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -940,5 +936,8 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -112,7 +112,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -128,7 +128,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -138,7 +138,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -197,7 +197,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -213,7 +213,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -223,7 +223,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -269,17 +269,12 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -337,7 +332,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -349,13 +344,12 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -363,7 +357,6 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -422,7 +415,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -438,7 +431,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -448,7 +441,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -530,6 +523,9 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -664,7 +660,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-14-live-region"
id="react-select-download-mirror-input-live-region"
/>
<span
aria-atomic="false"
@ -680,7 +676,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-14-placeholder"
id="react-select-download-mirror-input-placeholder"
>
Download mirror for kubectl
</div>
@ -690,7 +686,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
>
<input
aria-autocomplete="list"
aria-describedby="react-select-14-placeholder"
aria-describedby="react-select-download-mirror-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -841,11 +837,11 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
class="flex gaps"
>
<div
class="Select theme-lens box grow css-b62m3t-container"
class="Select theme-lens box grow Select--is-disabled css-3iigni-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-15-live-region"
id="react-select-HelmRepoSelect-live-region"
/>
<span
aria-atomic="false"
@ -854,30 +850,31 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
class="Select__control Select__control--is-disabled css-1insrsq-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-15-placeholder"
id="react-select-HelmRepoSelect-placeholder"
>
Repositories
</div>
<div
class="Select__input-container css-6j8wv5-Input"
class="Select__input-container css-jzldcf-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-15-placeholder"
aria-describedby="react-select-HelmRepoSelect-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
disabled=""
id="HelmRepoSelect"
role="combobox"
spellcheck="false"
@ -891,8 +888,22 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<div
aria-hidden="true"
class="Select__indicator Select__loading-indicator css-at12u2-loadingIndicator"
>
<span
class="css-1xtdfmb-LoadingDot"
/>
<span
class="css-zoievk-LoadingDot"
/>
<span
class="css-x748d8-LoadingDot"
/>
</div>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
class="Select__indicator-separator css-109onse-indicatorSeparator"
/>
<div
aria-hidden="true"
@ -925,13 +936,11 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
class="repos"
>
<div
class="notice"
class="pt-5 relative"
>
<div
class="flex-grow text-center"
>
The repositories have not been added yet
</div>
class="Spinner singleColor center"
/>
</div>
</div>
</div>
@ -974,5 +983,8 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -112,7 +112,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -128,7 +128,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -138,7 +138,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -197,7 +197,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -213,7 +213,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -223,7 +223,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -269,17 +269,12 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -337,7 +332,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -349,13 +344,12 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -363,7 +357,6 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -422,7 +415,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -438,7 +431,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -448,7 +441,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -530,6 +523,9 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -732,5 +728,8 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -185,6 +185,9 @@ exports[`preferences - navigation to telemetry preferences given URL for Sentry
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -300,7 +303,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -316,7 +319,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -326,7 +329,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -385,7 +388,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -401,7 +404,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -411,7 +414,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -457,17 +460,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -525,7 +523,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -537,13 +535,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -551,7 +548,6 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -610,7 +606,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -626,7 +622,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -636,7 +632,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -718,6 +714,9 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -845,7 +844,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-14-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -861,7 +860,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-14-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -871,7 +870,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-14-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -930,7 +929,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-15-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -946,7 +945,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-15-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -956,7 +955,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-15-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -1002,17 +1001,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -1070,7 +1064,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-16-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -1082,13 +1076,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-16-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -1096,7 +1089,6 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-16-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -1155,7 +1147,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-17-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -1171,7 +1163,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-17-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -1181,7 +1173,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-17-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -1263,6 +1255,9 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1439,6 +1434,9 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -1578,5 +1576,8 @@ exports[`preferences - navigation to telemetry preferences given no URL for Sent
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -112,7 +112,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -128,7 +128,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -138,7 +138,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -197,7 +197,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -213,7 +213,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -223,7 +223,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -269,17 +269,12 @@ exports[`preferences - navigation to terminal preferences given in preferences,
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -337,7 +332,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -349,13 +344,12 @@ exports[`preferences - navigation to terminal preferences given in preferences,
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -363,7 +357,6 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -422,7 +415,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -438,7 +431,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -448,7 +441,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -530,6 +523,9 @@ exports[`preferences - navigation to terminal preferences given in preferences,
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
@ -689,7 +685,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-14-live-region"
id="react-select-terminal-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -705,7 +701,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-14-placeholder"
id="react-select-terminal-theme-input-placeholder"
>
Select...
</div>
@ -715,7 +711,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<input
aria-autocomplete="list"
aria-describedby="react-select-14-placeholder"
aria-describedby="react-select-terminal-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -775,6 +771,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
>
<input
class="input box grow"
max="50"
min="10"
spellcheck="false"
type="number"
@ -794,22 +791,78 @@ exports[`preferences - navigation to terminal preferences given in preferences,
</div>
<div
class="Input theme round black"
class="Select theme-lens css-b62m3t-container"
>
<label
class="input-area flex gaps align-center"
id=""
>
<input
class="input box grow"
spellcheck="false"
type="text"
value=""
/>
</label>
<div
class="input-info flex gaps"
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
>
Select...
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="react-select-2-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</section>
</section>
@ -850,5 +903,8 @@ exports[`preferences - navigation to terminal preferences given in preferences,
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -1,6 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`preferences - navigation using application menu renders 1`] = `<div />`;
exports[`preferences - navigation using application menu renders 1`] = `
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
exports[`preferences - navigation using application menu when navigating to preferences using application menu renders 1`] = `
<div>
@ -114,7 +120,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-2-live-region"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
@ -130,7 +136,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-2-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
@ -140,7 +146,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<input
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -199,7 +205,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-3-live-region"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
@ -215,7 +221,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-3-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
@ -225,7 +231,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<input
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -271,17 +277,12 @@ exports[`preferences - navigation using application menu when navigating to pref
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (
https://registry.npmjs.org
)
you can change it in your
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
 file or in the input below.
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
@ -339,7 +340,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-4-live-region"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
@ -351,13 +352,12 @@ exports[`preferences - navigation using application menu when navigating to pref
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-4-placeholder"
class="Select__single-value css-qc6sy-singleValue"
>
Select...
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
@ -365,7 +365,6 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<input
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -424,7 +423,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-5-live-region"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
@ -440,7 +439,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-5-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
@ -450,7 +449,7 @@ exports[`preferences - navigation using application menu when navigating to pref
>
<input
aria-autocomplete="list"
aria-describedby="react-select-5-placeholder"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
@ -532,5 +531,8 @@ exports[`preferences - navigation using application menu when navigating to pref
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -0,0 +1,542 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`show-about-using-tray renders 1`] = `
<body>
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;
exports[`show-about-using-tray when navigating using tray renders 1`] = `
<body>
<div>
<div
class="SettingLayout showNavigation Preferences"
data-testid="application-preferences-page"
>
<nav
class="sidebarRegion"
>
<div
class="sidebar"
>
<div
class="Tabs flex column"
>
<div
class="header"
>
Preferences
</div>
<div
class="Tab flex gaps align-center active"
data-testid="tab-link-for-application"
role="tab"
tabindex="0"
>
<div
class="label"
>
App
</div>
</div>
<div
class="Tab flex gaps align-center"
data-testid="tab-link-for-proxy"
role="tab"
tabindex="0"
>
<div
class="label"
>
Proxy
</div>
</div>
<div
class="Tab flex gaps align-center"
data-testid="tab-link-for-kubernetes"
role="tab"
tabindex="0"
>
<div
class="label"
>
Kubernetes
</div>
</div>
<div
class="Tab flex gaps align-center"
data-testid="tab-link-for-editor"
role="tab"
tabindex="0"
>
<div
class="label"
>
Editor
</div>
</div>
<div
class="Tab flex gaps align-center"
data-testid="tab-link-for-terminal"
role="tab"
tabindex="0"
>
<div
class="label"
>
Terminal
</div>
</div>
</div>
</div>
</nav>
<div
class="contentRegion"
id="ScrollSpyRoot"
>
<div
class="content"
>
<section
id="application"
>
<h2
data-testid="application-header"
>
Application
</h2>
<section
id="appearance"
>
<div
class="SubTitle"
>
Theme
</div>
<div
class="Select theme-lens css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-theme-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-theme-input-placeholder"
>
Select...
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-theme-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="theme-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</section>
<hr />
<section
id="extensionRegistryUrl"
>
<div
class="SubTitle"
>
Extension Install Registry
</div>
<div
class="Select theme-lens css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-extension-install-registry-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-extension-install-registry-input-placeholder"
>
Select...
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-extension-install-registry-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="extension-install-registry-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
<p
class="mt-4 mb-5 leading-relaxed"
>
This setting is to change the registry URL for installing extensions by name.
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
<b>
.npmrc
</b>
file or in the input below.
</p>
<div
class="Input theme round black disabled invalid"
>
<label
class="input-area flex gaps align-center"
id=""
>
<input
class="input box grow"
disabled=""
placeholder="Custom Extension Registry URL..."
spellcheck="false"
value="some-custom-url"
/>
</label>
<div
class="input-info flex gaps"
/>
</div>
</section>
<hr />
<section
id="other"
>
<div
class="SubTitle"
>
Start-up
</div>
<label
class="Switch"
data-testid="switch"
>
Automatically start Lens on login
<input
role="switch"
type="checkbox"
/>
</label>
</section>
<hr />
<section
id="update-channel"
>
<div
class="SubTitle"
>
Update Channel
</div>
<div
class="Select theme-lens css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-update-channel-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
>
<div
class="Select__single-value css-qc6sy-singleValue"
>
Stable
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="update-channel-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</section>
<hr />
<section
id="locale"
>
<div
class="SubTitle"
>
Locale Timezone
</div>
<div
class="Select theme-lens css-b62m3t-container"
>
<span
class="css-1f43avz-a11yText-A11yText"
id="react-select-timezone-input-live-region"
/>
<span
aria-atomic="false"
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
/>
<div
class="Select__control css-1s2u09g-control"
>
<div
class="Select__value-container css-319lph-ValueContainer"
>
<div
class="Select__placeholder css-14el2xx-placeholder"
id="react-select-timezone-input-placeholder"
>
Select...
</div>
<div
class="Select__input-container css-6j8wv5-Input"
data-value=""
>
<input
aria-autocomplete="list"
aria-describedby="react-select-timezone-input-placeholder"
aria-expanded="false"
aria-haspopup="true"
autocapitalize="none"
autocomplete="off"
autocorrect="off"
class="Select__input"
id="timezone-input"
role="combobox"
spellcheck="false"
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
tabindex="0"
type="text"
value=""
/>
</div>
</div>
<div
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
>
<span
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
/>
<div
aria-hidden="true"
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
>
<svg
aria-hidden="true"
class="css-tj5bde-Svg"
focusable="false"
height="20"
viewBox="0 0 20 20"
width="20"
>
<path
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
/>
</svg>
</div>
</div>
</div>
</div>
</section>
</section>
</div>
<div
class="toolsRegion"
>
<div
class="fixed top-[60px]"
>
<div
data-testid="close-preferences"
>
<div
aria-label="Close"
class="closeButton"
role="button"
>
<i
class="Icon icon material focusable"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
</div>
<div
aria-hidden="true"
class="esc"
>
ESC
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
</body>
`;

View File

@ -10,10 +10,6 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
import currentPathInjectable from "../../renderer/routes/current-path.injectable";
import { routeInjectionToken } from "../../common/front-end-routing/route-injection-token";
import { computed } from "mobx";
import type { UserStore } from "../../common/user-store";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import { preferenceNavigationItemInjectionToken } from "../../renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable";
import routeIsActiveInjectable from "../../renderer/routes/route-is-active.injectable";
import { Preferences } from "../../renderer/components/+preferences";
@ -33,23 +29,13 @@ describe("preferences - closing-preferences", () => {
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.register(testPreferencesRouteInjectable);
rendererDi.register(testPreferencesRouteComponentInjectable);
rendererDi.register(testFrontPageRouteInjectable);
rendererDi.register(testFrontPageRouteComponentInjectable);
rendererDi.register(testNavigationItemInjectable);
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
rendererDi.override(navigateToFrontPageInjectable, (di) => {
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
const testFrontPage = di.inject(testFrontPageRouteInjectable);
@ -66,7 +52,7 @@ describe("preferences - closing-preferences", () => {
let rendererDi: DiContainer;
beforeEach(async () => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(observableHistoryInjectable, () => {
const historyFake = createMemoryHistory({
initialEntries: ["/some-test-path"],
@ -139,7 +125,7 @@ describe("preferences - closing-preferences", () => {
let rendererDi: DiContainer;
beforeEach(async () => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(observableHistoryInjectable, () => {
const historyFake = createMemoryHistory({
initialEntries: ["/preferences/app"],

View File

@ -5,30 +5,13 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { UserStore } from "../../common/user-store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import navigateToProxyPreferencesInjectable
from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable";
import navigateToProxyPreferencesInjectable from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable";
describe("preferences - navigation to application preferences", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in some child page of preferences, when rendered", () => {

View File

@ -5,29 +5,12 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { UserStore } from "../../common/user-store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
describe("preferences - navigation to editor preferences", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
editorConfiguration: { minimap: {}, tabSize: 42, fontSize: 42 },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in preferences, when rendered", () => {

View File

@ -5,32 +5,16 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { UserStore } from "../../common/user-store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
import React from "react";
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import "@testing-library/jest-dom/extend-expect";
import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
describe("preferences - navigation to extension specific preferences", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in preferences, when rendered", () => {
@ -62,6 +46,7 @@ describe("preferences - navigation to extension specific preferences", () => {
describe("given multiple extensions with specific preferences, when navigating to extension specific preferences page", () => {
beforeEach(async () => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const someTestExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
const someOtherTestExtension = getRendererExtensionFake(someOtherExtensionStubWithExtensionSpecificPreferenceItems);
@ -88,6 +73,7 @@ describe("preferences - navigation to extension specific preferences", () => {
describe("given multiple extensions with and without specific preferences, when navigating to extension specific preferences page", () => {
beforeEach(async () => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const someTestExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
const extensionWithoutPreferences = getRendererExtensionFake(extensionStubWithoutPreferences);
const extensionWithSpecificTab = getRendererExtensionFake(extensionStubWithShowInPreferencesTab);
@ -109,10 +95,11 @@ describe("preferences - navigation to extension specific preferences", () => {
});
describe("when extension with specific preferences is enabled", () => {
beforeEach(async () => {
beforeEach(() => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const testExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
await applicationBuilder.addExtensions(testExtension);
applicationBuilder.addExtensions(testExtension);
});
it("renders", () => {
@ -168,6 +155,7 @@ describe("preferences - navigation to extension specific preferences", () => {
describe("given extension with registered tab", () => {
beforeEach(async () => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const extension = getRendererExtensionFake(extensionStubWithWithRegisteredTab);
await applicationBuilder.addExtensions(extension);
@ -207,6 +195,7 @@ describe("preferences - navigation to extension specific preferences", () => {
describe("given extension with few registered tabs", () => {
beforeEach(async () => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const extension = getRendererExtensionFake(extensionStubWithWithRegisteredTabs);
await applicationBuilder.addExtensions(extension);
@ -223,6 +212,7 @@ describe("preferences - navigation to extension specific preferences", () => {
describe("given extensions with tabs having same id", () => {
beforeEach(async () => {
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const extension = getRendererExtensionFake(extensionStubWithWithRegisteredTab);
const otherExtension = getRendererExtensionFake(extensionStubWithWithSameRegisteredTab);
@ -288,9 +278,9 @@ describe("preferences - navigation to extension specific preferences", () => {
});
});
const extensionStubWithExtensionSpecificPreferenceItems: Partial<LensRendererExtension> = {
id: "some-test-extension-id",
const extensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
id: "some-extension-id",
name: "some-extension-name",
appPreferences: [
{
title: "Some preference item",
@ -315,8 +305,9 @@ const extensionStubWithExtensionSpecificPreferenceItems: Partial<LensRendererExt
],
};
const someOtherExtensionStubWithExtensionSpecificPreferenceItems: Partial<LensRendererExtension> = {
const someOtherExtensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
id: "some-other-test-extension-id",
name: "some-other-test-extension-name",
appPreferences: [
{
@ -331,12 +322,14 @@ const someOtherExtensionStubWithExtensionSpecificPreferenceItems: Partial<LensRe
],
};
const extensionStubWithoutPreferences: Partial<LensRendererExtension> = {
const extensionStubWithoutPreferences: FakeExtensionData = {
id: "without-preferences-id",
name: "without-preferences-name",
};
const extensionStubWithShowInPreferencesTab: Partial<LensRendererExtension> = {
const extensionStubWithShowInPreferencesTab: FakeExtensionData = {
id: "specified-preferences-page-id",
name: "specified-preferences-page-name",
appPreferences: [
{
@ -352,8 +345,9 @@ const extensionStubWithShowInPreferencesTab: Partial<LensRendererExtension> = {
],
};
const extensionStubWithWithRegisteredTab: Partial<LensRendererExtension> = {
const extensionStubWithWithRegisteredTab: FakeExtensionData = {
id: "registered-tab-page-id",
name: "registered-tab-page-name",
appPreferences: [
{
@ -395,8 +389,9 @@ const extensionStubWithWithRegisteredTab: Partial<LensRendererExtension> = {
}],
};
const extensionStubWithWithRegisteredTabs: Partial<LensRendererExtension> = {
const extensionStubWithWithRegisteredTabs: FakeExtensionData = {
id: "hello-world-tab-page-id",
name: "hello-world-tab-page-name",
appPreferences: [
{
@ -432,8 +427,9 @@ const extensionStubWithWithRegisteredTabs: Partial<LensRendererExtension> = {
}],
};
const extensionStubWithWithSameRegisteredTab: Partial<LensRendererExtension> = {
const extensionStubWithWithSameRegisteredTab: FakeExtensionData = {
id: "duplicated-tab-page-id",
name: "duplicated-tab-page-name",
appPreferences: [
{

View File

@ -5,30 +5,12 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { UserStore } from "../../common/user-store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import { observable } from "mobx";
describe("preferences - navigation to kubernetes preferences", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
syncKubeconfigEntries: observable.map(),
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in preferences, when rendered", () => {

View File

@ -5,28 +5,12 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { UserStore } from "../../common/user-store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
describe("preferences - navigation to proxy preferences", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in preferences, when rendered", () => {

View File

@ -6,11 +6,8 @@ import type { RenderResult } from "@testing-library/react";
import React from "react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import type { UserStore } from "../../common/user-store";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
import navigateToTelemetryPreferencesInjectable from "../../common/front-end-routing/routes/preferences/telemetry/navigate-to-telemetry-preferences.injectable";
import sentryDnsUrlInjectable from "../../renderer/components/+preferences/sentry-dns-url.injectable";
@ -19,18 +16,6 @@ describe("preferences - navigation to telemetry preferences", () => {
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in preferences, when rendered", () => {
@ -62,8 +47,8 @@ describe("preferences - navigation to telemetry preferences", () => {
describe("when extension with telemetry preference items gets enabled", () => {
beforeEach(() => {
const testExtensionWithTelemetryPreferenceItems =
getRendererExtensionFake(extensionStubWithTelemetryPreferenceItems);
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const testExtensionWithTelemetryPreferenceItems = getRendererExtensionFake(extensionStubWithTelemetryPreferenceItems);
applicationBuilder.addExtensions(
testExtensionWithTelemetryPreferenceItems,
@ -106,18 +91,19 @@ describe("preferences - navigation to telemetry preferences", () => {
});
it("given extensions but no telemetry preference items, does not show link for telemetry preferences", () => {
const testExtensionWithTelemetryPreferenceItems =
getRendererExtensionFake({
id: "some-test-extension-id",
appPreferences: [
{
title: "irrelevant",
id: "irrelevant",
showInPreferencesTab: "not-telemetry",
components: { Hint: () => <div />, Input: () => <div /> },
},
],
});
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
const testExtensionWithTelemetryPreferenceItems = getRendererExtensionFake({
id: "some-test-extension-id",
name: "some-test-extension-name",
appPreferences: [
{
title: "irrelevant",
id: "irrelevant",
showInPreferencesTab: "not-telemetry",
components: { Hint: () => <div />, Input: () => <div /> },
},
],
});
applicationBuilder.addExtensions(
testExtensionWithTelemetryPreferenceItems,
@ -133,7 +119,7 @@ describe("preferences - navigation to telemetry preferences", () => {
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(sentryDnsUrlInjectable, () => "some-sentry-dns-url");
});
@ -163,7 +149,7 @@ describe("preferences - navigation to telemetry preferences", () => {
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder.beforeSetups(({ rendererDi }) => {
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
rendererDi.override(sentryDnsUrlInjectable, () => null);
});
@ -186,8 +172,9 @@ describe("preferences - navigation to telemetry preferences", () => {
});
});
const extensionStubWithTelemetryPreferenceItems = {
const extensionStubWithTelemetryPreferenceItems: FakeExtensionData = {
id: "some-test-extension-id",
name: "some-test-extension-name",
appPreferences: [
{
title: "Some telemetry-preference item",

View File

@ -5,34 +5,12 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { UserStore } from "../../common/user-store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import { observable } from "mobx";
import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable";
describe("preferences - navigation to terminal preferences", () => {
let applicationBuilder: ApplicationBuilder;
beforeEach(() => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi }) => {
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
syncKubeconfigEntries: observable.map(),
terminalConfig: { fontSize: 42 },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
rendererDi.override(defaultShellInjectable, () => "some-default-shell");
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
});
describe("given in preferences, when rendered", () => {

View File

@ -6,11 +6,6 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
import type { UserStore } from "../../common/user-store";
import userStoreInjectable from "../../common/user-store/user-store.injectable";
import type { ThemeStore } from "../../renderer/theme.store";
import themeStoreInjectable from "../../renderer/theme-store.injectable";
describe("preferences - navigation using application menu", () => {
let applicationBuilder: ApplicationBuilder;
@ -19,20 +14,6 @@ describe("preferences - navigation using application menu", () => {
beforeEach(async () => {
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeSetups(({ rendererDi, mainDi }) => {
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
const userStoreStub = {
extensionRegistryUrl: { customUrl: "some-custom-url" },
} as unknown as UserStore;
rendererDi.override(userStoreInjectable, () => userStoreStub);
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
});
rendered = await applicationBuilder.render();
});
@ -47,8 +28,8 @@ describe("preferences - navigation using application menu", () => {
});
describe("when navigating to preferences using application menu", () => {
beforeEach(() => {
applicationBuilder.applicationMenu.click("root.preferences");
beforeEach(async () => {
await applicationBuilder.applicationMenu.click("root.preferences");
});
it("renders", () => {

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
describe("show-about-using-tray", () => {
let applicationBuilder: ApplicationBuilder;
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder = getApplicationBuilder();
rendered = await applicationBuilder.render();
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("does not show application preferences page yet", () => {
const actual = rendered.queryByTestId("application-preferences-page");
expect(actual).toBeNull();
});
describe("when navigating using tray", () => {
beforeEach(async () => {
await applicationBuilder.tray.click("open-preferences");
});
it("renders", () => {
expect(rendered.baseElement).toMatchSnapshot();
});
it("shows application preferences page", () => {
const actual = rendered.getByTestId("application-preferences-page");
expect(actual).not.toBeNull();
});
});
});

12
src/behaviours/utils.ts Normal file
View File

@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RenderResult } from "@testing-library/react";
export function getSidebarItem(rendered: RenderResult, itemId: string) {
return rendered
.queryAllByTestId("sidebar-item")
.find((x) => x.dataset.idTest === itemId);
}

View File

@ -1,6 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`welcome - navigation using application menu renders 1`] = `<div />`;
exports[`welcome - navigation using application menu renders 1`] = `
<div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;
exports[`welcome - navigation using application menu when navigating to welcome using application menu renders 1`] = `
<div>
@ -27,16 +33,17 @@ exports[`welcome - navigation using application menu when navigating to welcome
style="width: 320px;"
>
<h2>
Welcome to
OpenLens
5!
Welcome to OpenLens 5!
</h2>
<p>
To get you started we have auto-detected your clusters in your kubeconfig file and added them to the catalog, your centralized view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
To get you started we have auto-detected your clusters in your
kubeconfig file and added them to the catalog, your centralized
view for managing all your cloud-native resources.
<br />
<br />
If you have any questions or feedback, please join our
<a
class="link"
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
@ -86,5 +93,8 @@ exports[`welcome - navigation using application menu when navigating to welcome
</div>
</div>
</div>
<div
class="Notifications flex column align-flex-end"
/>
</div>
`;

View File

@ -6,16 +6,13 @@
import type { RenderResult } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
describe("welcome - navigation using application menu", () => {
let applicationBuilder: ApplicationBuilder;
let rendered: RenderResult;
beforeEach(async () => {
applicationBuilder = getApplicationBuilder().beforeSetups(({ mainDi }) => {
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
});
applicationBuilder = getApplicationBuilder();
rendered = await applicationBuilder.render();
});
@ -31,8 +28,8 @@ describe("welcome - navigation using application menu", () => {
});
describe("when navigating to welcome using application menu", () => {
beforeEach(() => {
applicationBuilder.applicationMenu.click("help.welcome");
beforeEach(async () => {
await applicationBuilder.applicationMenu.click("help.welcome");
});
it("renders", () => {

View File

@ -30,9 +30,9 @@ interface TestStoreModel {
}
class TestStore extends BaseStore<TestStoreModel> {
@observable a: string;
@observable b: string;
@observable c: string;
@observable a = "";
@observable b = "";
@observable c = "";
constructor() {
super({
@ -81,16 +81,13 @@ class TestStore extends BaseStore<TestStoreModel> {
describe("BaseStore", () => {
let store: TestStore;
beforeEach(async () => {
beforeEach(() => {
const mainDi = getDiForUnitTesting({ doGeneralOverrides: true });
mainDi.override(directoryForUserDataInjectable, () => "some-user-data-directory");
mainDi.permitSideEffects(getConfigurationFileModelInjectable);
mainDi.permitSideEffects(appVersionInjectable);
await mainDi.runSetups();
store = undefined;
TestStore.resetInstance();
const mockOpts = {

View File

@ -8,22 +8,23 @@ import mockFs from "mock-fs";
import path from "path";
import fse from "fs-extra";
import type { Cluster } from "../cluster/cluster";
import { ClusterStore } from "../cluster-store/cluster-store";
import type { ClusterStore } from "../cluster-store/cluster-store";
import { Console } from "console";
import { stdout, stderr } from "process";
import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
import type { ClusterModel } from "../cluster-types";
import type {
DiContainer,
} from "@ogre-tools/injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
import assert from "assert";
import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable";
import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable";
import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable";
import normalizedPlatformInjectable from "../vars/normalized-platform.injectable";
console = new Console(stdout, stderr);
@ -84,15 +85,17 @@ describe("cluster-store", () => {
mockFs();
mainDi.override(clusterStoreInjectable, (di) => ClusterStore.createInstance({ createCluster: di.inject(createClusterInjectionToken) }));
mainDi.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
mainDi.override(directoryForTempInjectable, () => "some-temp-directory");
mainDi.override(kubectlBinaryNameInjectable, () => "kubectl");
mainDi.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64");
mainDi.override(normalizedPlatformInjectable, () => "darwin");
mainDi.permitSideEffects(getConfigurationFileModelInjectable);
mainDi.permitSideEffects(appVersionInjectable);
mainDi.permitSideEffects(clusterStoreInjectable);
await mainDi.runSetups();
createCluster = mainDi.inject(createClusterInjectionToken);
mainDi.unoverride(clusterStoreInjectable);
});
afterEach(() => {
@ -107,10 +110,6 @@ describe("cluster-store", () => {
getCustomKubeConfigDirectoryInjectable,
);
// TODO: Remove these by removing Singleton base-class from BaseStore
ClusterStore.getInstance(false)?.unregisterIpcListener();
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({}),
@ -119,7 +118,11 @@ describe("cluster-store", () => {
mockFs(mockOpts);
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
clusterStore.unregisterIpcListener();
});
afterEach(() => {
@ -148,6 +151,8 @@ describe("cluster-store", () => {
it("adds new cluster to store", async () => {
const storedCluster = clusterStore.getById("foo");
assert(storedCluster);
expect(storedCluster.id).toBe("foo");
expect(storedCluster.preferences.terminalCWD).toBe("/some-directory-for-user-data");
expect(storedCluster.preferences.icon).toBe(
@ -199,8 +204,6 @@ describe("cluster-store", () => {
describe("config with existing clusters", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"temp-kube-config": kubeconfig,
"some-directory-for-user-data": {
@ -239,6 +242,8 @@ describe("cluster-store", () => {
mockFs(mockOpts);
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
@ -249,6 +254,8 @@ describe("cluster-store", () => {
it("allows to retrieve a cluster", () => {
const storedCluster = clusterStore.getById("cluster1");
assert(storedCluster);
expect(storedCluster.id).toBe("cluster1");
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
});
@ -287,8 +294,6 @@ users:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
ClusterStore.resetInstance();
const mockOpts = {
"invalid-kube-config": invalidKubeconfig,
"valid-kube-config": kubeconfig,
@ -321,6 +326,8 @@ users:
mockFs(mockOpts);
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
@ -337,7 +344,6 @@ users:
describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance();
const mockOpts = {
"some-directory-for-user-data": {
"lens-cluster-store.json": JSON.stringify({
@ -363,6 +369,10 @@ users:
mockFs(mockOpts);
mainDi.override(appVersionInjectable, () => "3.6.0");
createCluster = mainDi.inject(createClusterInjectionToken);
clusterStore = mainDi.inject(clusterStoreInjectable);
});
@ -379,6 +389,7 @@ users:
it("migrates to modern format with icon not in file", async () => {
const { icon } = clusterStore.clustersList[0].preferences;
assert(icon);
expect(icon.startsWith("data:;base64,")).toBe(true);
});
});

View File

@ -5,7 +5,7 @@
import type { AppEvent } from "../app-event-bus/event-bus";
import { appEventBus } from "../app-event-bus/event-bus";
import { Console } from "console";
import { assert, Console } from "console";
import { stdout, stderr } from "process";
console = new Console(stdout, stderr);
@ -13,14 +13,15 @@ console = new Console(stdout, stderr);
describe("event bus tests", () => {
describe("emit", () => {
it("emits an event", () => {
let event: AppEvent = null;
let event: AppEvent | undefined;
appEventBus.addListener((data) => {
event = data;
});
appEventBus.emit({ name: "foo", action: "bar" });
expect(event.name).toBe("foo");
assert(event);
expect(event?.name).toBe("foo");
});
});
});

View File

@ -21,7 +21,7 @@ describe("EventEmitter", () => {
let called = false;
const e = new EventEmitter<[]>();
e.addListener(() => 0 as any, {});
e.addListener(() => 0 as never, {});
e.addListener(() => { called = true; }, {});
e.emit();

View File

@ -5,69 +5,20 @@
import { anyObject } from "jest-mock-extended";
import mockFs from "mock-fs";
import logger from "../../main/logger";
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import hotbarStoreInjectable from "../hotbar-store.injectable";
import { HotbarStore } from "../hotbar-store";
import hotbarStoreInjectable from "../hotbars/store.injectable";
import type { HotbarStore } from "../hotbars/store";
import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable";
import { computed } from "mobx";
import hasCategoryForEntityInjectable from "../catalog/has-category-for-entity.injectable";
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
jest.mock("../../main/catalog/catalog-entity-registry", () => ({
catalogEntityRegistry: {
items: [
getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
name: "mycluster",
source: "local",
labels: {},
},
}),
getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "55b42c3c7ba3b04193416cda405269a5",
name: "my_shiny_cluster",
source: "remote",
labels: {},
},
}),
getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "catalog-entity",
name: "Catalog",
source: "app",
labels: {},
},
}),
],
},
}));
import loggerInjectable from "../logger.injectable";
import type { Logger } from "../logger";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
return {
@ -84,75 +35,99 @@ function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKi
} as CatalogEntity;
}
const testCluster = getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "test",
name: "test",
labels: {},
},
});
const minikubeCluster = getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "minikube",
name: "minikube",
labels: {},
},
});
const awsCluster = getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "aws",
name: "aws",
labels: {},
},
});
describe("HotbarStore", () => {
let di: DiContainer;
let hotbarStore: HotbarStore;
let testCluster: CatalogEntity;
let minikubeCluster: CatalogEntity;
let awsCluster: CatalogEntity;
let loggerMock: jest.Mocked<Logger>;
beforeEach(async () => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
(di as any).unoverride(hotbarStoreInjectable);
testCluster = getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "some-test-id",
name: "my-test-cluster",
source: "local",
labels: {},
},
});
minikubeCluster = getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "some-minikube-id",
name: "my-minikube-cluster",
source: "local",
labels: {},
},
});
awsCluster = getMockCatalogEntity({
apiVersion: "v1",
kind: "Cluster",
status: {
phase: "Running",
},
metadata: {
uid: "some-aws-id",
name: "my-aws-cluster",
source: "local",
labels: {},
},
});
di.override(hasCategoryForEntityInjectable, () => () => true);
loggerMock = {
warn: jest.fn(),
debug: jest.fn(),
error: jest.fn(),
info: jest.fn(),
silly: jest.fn(),
};
di.override(loggerInjectable, () => loggerMock);
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
catalogEntityRegistry.addComputedSource("some-id", computed(() => [
testCluster,
minikubeCluster,
awsCluster,
catalogCatalogEntity,
]));
di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable);
di.override(hotbarStoreInjectable, () => {
HotbarStore.resetInstance();
return HotbarStore.createInstance({
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
});
});
di.permitSideEffects(hotbarStoreInjectable);
});
afterEach(() => {
mockFs.restore();
});
describe("given no migrations", () => {
beforeEach(async () => {
describe("given no previous data in store, running all migrations", () => {
beforeEach(() => {
mockFs();
await di.runSetups();
hotbarStore = di.inject(hotbarStoreInjectable);
hotbarStore.load();
});
describe("load", () => {
@ -174,7 +149,7 @@ describe("HotbarStore", () => {
});
it("initially adds catalog entity as first item", () => {
expect(hotbarStore.getActive().items[0].entity.name).toEqual("Catalog");
expect(hotbarStore.getActive().items[0]?.entity.name).toEqual("Catalog");
});
it("adds items", () => {
@ -186,7 +161,7 @@ describe("HotbarStore", () => {
it("removes items", () => {
hotbarStore.addToHotbar(testCluster);
hotbarStore.removeFromHotbar("test");
hotbarStore.removeFromHotbar("some-test-id");
hotbarStore.removeFromHotbar("catalog-entity");
const items = hotbarStore.getActive().items.filter(Boolean);
@ -211,7 +186,7 @@ describe("HotbarStore", () => {
hotbarStore.restackItems(1, 5);
expect(hotbarStore.getActive().items[5]).toBeTruthy();
expect(hotbarStore.getActive().items[5].entity.uid).toEqual("test");
expect(hotbarStore.getActive().items[5]?.entity.uid).toEqual("some-test-id");
});
it("moves items down", () => {
@ -224,7 +199,7 @@ describe("HotbarStore", () => {
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
expect(items.slice(0, 4)).toEqual(["aws", "catalog-entity", "test", "minikube"]);
expect(items.slice(0, 4)).toEqual(["some-aws-id", "catalog-entity", "some-test-id", "some-minikube-id"]);
});
it("moves items up", () => {
@ -237,28 +212,21 @@ describe("HotbarStore", () => {
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
expect(items.slice(0, 4)).toEqual(["catalog-entity", "minikube", "aws", "test"]);
expect(items.slice(0, 4)).toEqual(["catalog-entity", "some-minikube-id", "some-aws-id", "some-test-id"]);
});
it("logs an error if cellIndex is out of bounds", () => {
hotbarStore.add({ name: "hottest", id: "hottest" });
hotbarStore.setActiveHotbar("hottest");
const { error } = logger;
const mocked = jest.fn();
logger.error = mocked;
hotbarStore.addToHotbar(testCluster, -1);
expect(mocked).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
hotbarStore.addToHotbar(testCluster, 12);
expect(mocked).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
hotbarStore.addToHotbar(testCluster, 13);
expect(mocked).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
logger.error = error;
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
});
it("throws an error if getId is invalid or returns not a string", () => {
@ -275,7 +243,7 @@ describe("HotbarStore", () => {
hotbarStore.addToHotbar(testCluster);
hotbarStore.restackItems(1, 1);
expect(hotbarStore.getActive().items[1].entity.uid).toEqual("test");
expect(hotbarStore.getActive().items[1]?.entity.uid).toEqual("some-test-id");
});
it("new items takes first empty cell", () => {
@ -284,7 +252,7 @@ describe("HotbarStore", () => {
hotbarStore.restackItems(0, 3);
hotbarStore.addToHotbar(minikubeCluster);
expect(hotbarStore.getActive().items[0].entity.uid).toEqual("minikube");
expect(hotbarStore.getActive().items[0]?.entity.uid).toEqual("some-minikube-id");
});
it("throws if invalid arguments provided", () => {
@ -315,10 +283,10 @@ describe("HotbarStore", () => {
});
});
describe("given pre beta-5 configurations", () => {
beforeEach(async () => {
describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => {
beforeEach(() => {
const configurationToBeMigrated = {
"some-electron-app-path-for-user-data": {
"some-directory-for-user-data": {
"lens-hotbar-store.json": JSON.stringify({
__internal__: {
migrations: {
@ -332,7 +300,7 @@ describe("HotbarStore", () => {
items: [
{
entity: {
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
uid: "some-aws-id",
},
},
{
@ -381,15 +349,17 @@ describe("HotbarStore", () => {
mockFs(configurationToBeMigrated);
await di.runSetups();
di.override(appVersionInjectable, () => "5.0.0-beta.10");
hotbarStore = di.inject(hotbarStoreInjectable);
hotbarStore.load();
});
it("allows to retrieve a hotbar", () => {
const hotbar = hotbarStore.getById("3caac17f-aec2-4723-9694-ad204465d935");
const hotbar = hotbarStore.findById("3caac17f-aec2-4723-9694-ad204465d935");
expect(hotbar.id).toBe("3caac17f-aec2-4723-9694-ad204465d935");
expect(hotbar?.id).toBe("3caac17f-aec2-4723-9694-ad204465d935");
});
it("clears cells without entity", () => {
@ -403,17 +373,9 @@ describe("HotbarStore", () => {
expect(items[0]).toEqual({
entity: {
name: "mycluster",
name: "my-aws-cluster",
source: "local",
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
},
});
expect(items[1]).toEqual({
entity: {
name: "my_shiny_cluster",
source: "remote",
uid: "55b42c3c7ba3b04193416cda405269a5",
uid: "some-aws-id",
},
});
});

View File

@ -6,13 +6,14 @@ import https from "https";
import os from "os";
import { getMacRootCA, getWinRootCA, injectCAs, DSTRootCAX3 } from "../system-ca";
import { dependencies, devDependencies } from "../../../package.json";
import assert from "assert";
const deps = { ...dependencies, ...devDependencies };
// Skip the test if mac-ca is not installed, or os is not darwin
(deps["mac-ca"] && os.platform().includes("darwin") ? describe: describe.skip)("inject CA for Mac", () => {
// for reset https.globalAgent.options.ca after testing
let _ca: string | Buffer | (string | Buffer)[];
let _ca: string | Buffer | (string | Buffer)[] | undefined;
beforeEach(() => {
_ca = https.globalAgent.options.ca;
@ -44,6 +45,7 @@ const deps = { ...dependencies, ...devDependencies };
injectCAs(osxCAs);
const injected = https.globalAgent.options.ca;
assert(injected);
expect(injected.includes(DSTRootCAX3)).toBeFalsy();
});
});
@ -51,7 +53,7 @@ const deps = { ...dependencies, ...devDependencies };
// Skip the test if win-ca is not installed, or os is not win32
(deps["win-ca"] && os.platform().includes("win32") ? describe: describe.skip)("inject CA for Windows", () => {
// for reset https.globalAgent.options.ca after testing
let _ca: string | Buffer | (string | Buffer)[];
let _ca: string | Buffer | (string | Buffer)[] | undefined;
beforeEach(() => {
_ca = https.globalAgent.options.ca;

View File

@ -21,7 +21,7 @@ jest.mock("electron", () => ({
},
}));
import { UserStore } from "../user-store";
import type { UserStore } from "../user-store";
import { Console } from "console";
import { SemVer } from "semver";
import electron from "electron";
@ -30,13 +30,11 @@ import userStoreInjectable from "../user-store/user-store.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
import { defaultTheme } from "../vars";
import { defaultThemeId } from "../vars";
import writeFileInjectable from "../fs/write-file.injectable";
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
import getConfigurationFileModelInjectable
from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable
from "../get-configuration-file-model/app-version/app-version.injectable";
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
console = new Console(stdout, stderr);
@ -44,23 +42,22 @@ describe("user store tests", () => {
let userStore: UserStore;
let di: DiContainer;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
mockFs();
di.override(writeFileInjectable, () => () => undefined);
di.override(writeFileInjectable, () => () => Promise.resolve());
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(userStoreInjectable, () => UserStore.createInstance());
di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable);
await di.runSetups();
di.permitSideEffects(appVersionInjectable);
di.permitSideEffects(userStoreInjectable);
di.unoverride(userStoreInjectable);
});
afterEach(() => {
UserStore.resetInstance();
mockFs.restore();
});
@ -80,7 +77,7 @@ describe("user store tests", () => {
userStore.httpsProxy = "abcd://defg";
expect(userStore.httpsProxy).toBe("abcd://defg");
expect(userStore.colorTheme).toBe(defaultTheme);
expect(userStore.colorTheme).toBe(defaultThemeId);
userStore.colorTheme = "light";
expect(userStore.colorTheme).toBe("light");
@ -89,7 +86,7 @@ describe("user store tests", () => {
it("correctly resets theme to default value", async () => {
userStore.colorTheme = "some other theme";
userStore.resetTheme();
expect(userStore.colorTheme).toBe(defaultTheme);
expect(userStore.colorTheme).toBe(defaultThemeId);
});
it("correctly calculates if the last seen version is an old release", () => {
@ -130,6 +127,8 @@ describe("user store tests", () => {
},
});
di.override(appVersionInjectable, () => "10.0.0");
userStore = di.inject(userStoreInjectable);
});

View File

@ -8,6 +8,7 @@ import { appEventBus } from "./event-bus";
const appEventBusInjectable = getInjectable({
id: "app-event-bus",
instantiate: () => appEventBus,
causesSideEffects: true,
});
export default appEventBusInjectable;

View File

@ -4,12 +4,9 @@
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { PathName } from "./app-path-names";
import { createChannel } from "../ipc-channel/create-channel/create-channel";
export type AppPaths = Record<PathName, string>;
export const appPathsInjectionToken = getInjectionToken<AppPaths>({ id: "app-paths-token" });
export const appPathsIpcChannel = createChannel<AppPaths>("app-paths");

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { AppPaths } from "./app-path-injection-token";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
export type AppPathsChannel = RequestChannel<void, AppPaths>;
const appPathsChannelInjectable = getInjectable({
id: "app-paths-channel",
instantiate: (): AppPathsChannel => ({
id: "app-paths",
}),
injectionToken: messageChannelInjectionToken,
});
export default appPathsChannelInjectable;

View File

@ -0,0 +1,34 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { AppPaths } from "./app-path-injection-token";
const appPathsStateInjectable = getInjectable({
id: "app-paths-state",
instantiate: () => {
let state: AppPaths;
return {
get: () =>{
if (!state) {
throw new Error("Tried to get app paths before state is setupped.");
}
return state;
},
set: (newState: AppPaths) => {
if (state) {
throw new Error("Tried to overwrite existing state of app paths.");
}
state = newState;
},
};
},
});
export default appPathsStateInjectable;

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { appPathsInjectionToken } from "./app-path-injection-token";
import appPathsStateInjectable from "./app-paths-state.injectable";
const appPathsInjectable = getInjectable({
id: "app-paths",
instantiate: (di) => di.inject(appPathsStateInjectable).get(),
injectionToken: appPathsInjectionToken,
});
export default appPathsInjectable;

Some files were not shown because too many files have changed in this diff Show More