mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Turn on strict mode in tsconfig.json, some helpful lints, and required cleanup where strictness necessitates it (#5195)
* Turn on strict mode in tsconfig.json - Add route, clusterRoute, and payloadValidatedClusterRoute helper functions to improve types with backend routes - Turn on the following new lints: - react/jsx-first-prop-new-line - react/jsx-wrap-multilines - react/jsx-one-expression-per-line - react/jsx-max-props-per-line - react/jsx-indent - react/jsx-indent-props Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix build Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace KubeObject scope strings with enum Signed-off-by: Sebastian Malton <sebastian@malton.name> * Revert package.json version changes Signed-off-by: Sebastian Malton <sebastian@malton.name> * revert move hostedCluster(Id) Signed-off-by: Sebastian Malton <sebastian@malton.name> * change some type param names to be not single letters Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove copy-extension-themes Signed-off-by: Sebastian Malton <sebastian@malton.name> * add new make clean action Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix build to not use webpack for generating types Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube-object-menu.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix select.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * revert move fileNameMigration to index Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix ref logic error Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix log-resource-selector.test.tsx tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-store.test.ts test by overriding createStorage to not touch file system Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cluster.test.ts tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube=api.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fixed hotbar-store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kubeconfig-manager.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cluster-role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix pods.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix delete-cluster-dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix daemonset.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix replicaset.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix statefulsets/dialog/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix replicasets/scale-dialog/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix deployments.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix deployments/scale/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cronjob.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix stateful-set.api.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix deployment.api.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix api-manager.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix statefulset.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix job.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix pods.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix scroll-spy.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix hotbar-remove-command.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog-entity-registry.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix welcome.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix verify-that-all-routes-have-route-component.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix pod-tolerations.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * better fix for previous 3 fixes, plus also select.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube-object-menu.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix app-paths.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-tabs.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix isReactNode typing Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix sub-title.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix drawer.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix list-layout.tsx and header.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix error-boundary.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix upgrade-chart/store.ts and dock-tab.store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix install-chart/store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix edit-resource/store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix create-resource/store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix namespace-select.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix namespace-select-filter.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix crd-list.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix wrong types for extensions Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix circular dependency Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix circular dependency on catalogCategoryRegistry Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix api-kube Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix type errors, most <Select /> errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * fixing more type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * some more fixing type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * convert all KubeApis to injectable with legacy global backups Signed-off-by: Sebastian Malton <sebastian@malton.name> * factor out into a common file all the exports Signed-off-by: Sebastian Malton <sebastian@malton.name> * convert all KubeObjectStores to injectable with legacy global backups Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove unused legacy KubeApi globals Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix bad previous commit Signed-off-by: Sebastian Malton <sebastian@malton.name> * more crash fixing Signed-off-by: Sebastian Malton <sebastian@malton.name> * try and fix behavioural tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix sidebar-and-tab-navigation-for-core.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix sidebar-and-tab-navigation-for-extensions.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-using-application-menu.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make ThemeStore non-singleton and fix navigation-to-terminal-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * extensions.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog-entity-registry.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-using-application-menu.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix log-resource-selector.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-tabs.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix delete-cluster-dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-kubernetes-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-editor-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-proxy-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-using-application-menu.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-application-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix select.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix hotbar-remove-command.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cluster-role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-extension-specific-preferences.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-telemetry-preferences.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix closing-preferences.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-editor-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-proxy-preferences.test.ts - Fix other type errors too Signed-off-by: Sebastian Malton <sebastian@malton.name> * final tweaks Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add more tsconfig files, fix bug in <Catalog> - Make all of history, navigation injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert all of kube-details-params/ and navigate/ to injectable - This fixes a runtime error that was encountered during testing Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix runtime errors on renderer - remove all static uses of `createPageParam` (and then removed the legacy global) - Made LensRendererExtension and LensMainExtension just used dependencies and not the getLegacyDi - Fixed circular dep in extension-loader Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move registerStore calls to after injectMany Signed-off-by: Sebastian Malton <sebastian@malton.name> * replace all the rest of the legacy uses of apiManager Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix stack overflow and cycles in DI Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix NamespaceSelectFilter not opening Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix WizardStep and AddNamespaceDialog Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix KubeApi's not being registered Signed-off-by: Sebastian Malton <sebastian@malton.name> * cleanup WindowManager Signed-off-by: Sebastian Malton <sebastian@malton.name> * Proper fix for Wizard, fix NamespaceStore.subscribe Signed-off-by: Sebastian Malton <sebastian@malton.name> * Rewrite withTooltip to be more type correct - Fixes mobx related "too many recursive actions" error - Change all the uses of withTooltips to be functional components Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add e2e test to cover kube api registration Signed-off-by: Sebastian Malton <sebastian@malton.name> * cleanup internal-commands Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove cast in <Animate> Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix command-palette e2e test Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error after rebase Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test name Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix code to help CodeQL scanner Signed-off-by: Sebastian Malton <sebastian@malton.name> * update intree extension lock files Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix build-extensions picking wrong @types/react Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix tests from rebase Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make KubeconfigSyncManager more injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix crash in test mode for Dialog Signed-off-by: Sebastian Malton <sebastian@malton.name> * make Select snapshots deterministic Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix new type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube-object.store.test.ts typing Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix merge build issues Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix snapshots after merge Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix lint after merge Signed-off-by: Sebastian Malton <sebastian@malton.name> * reexport BaseKubeJsonApiObjectMetadata Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix typo in terminalSpawningPool Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove duplicate license header Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix typo to waitUntilDefined Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove iter use from getLegacyGlobalDiForExtensionApi Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove complex createStorage override Signed-off-by: Sebastian Malton <sebastian@malton.name> * override logger with mocks only when needed for tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove specialized overrideStore flags for getDiForUnitTesting Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove unnecessary | undefined types from the exactOptionalFieldTypes experiment Signed-off-by: Sebastian Malton <sebastian@malton.name> * use more descriptive name for local test mocks Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove unnecessary addition to 'make clean' target Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove oddity of KubeObjectStore.getById(undefined) being allowed Signed-off-by: Sebastian Malton <sebastian@malton.name> * rename KubeObject.getDescriptor in favour of name without fundemental JS meaning Signed-off-by: Sebastian Malton <sebastian@malton.name> * Simplify legacyRegisterApi when working in behaviour unit tests - Don't emit within main environment as there should be no auto registering there Signed-off-by: Sebastian Malton <sebastian@malton.name> * change confusing variable name in ReactiveDuration Signed-off-by: Sebastian Malton <sebastian@malton.name> * make visitor pattern more explicit for Entity contextMenuOpen Signed-off-by: Sebastian Malton <sebastian@malton.name> * toggleDetails -> toggleKubeDetailsPane is more specific Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove outdated comment Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix bug where LensExtension dependencies are not set Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests from the revert of react 18 Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix more tests from merge Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix typings with new is-compatible-extension tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * more type fixing Signed-off-by: Sebastian Malton <sebastian@malton.name> * Revert in-tree extension versions Signed-off-by: Sebastian Malton <sebastian@malton.name> * Improve name of guarding injectable for stores and apis - New name better implies that it is just a guard state and does not do anything Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add helper for <Select>.isMulti for storing in a Set<Value> Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix is-compatible-extension.test.ts types Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
381d77c633
commit
dfcb7c3330
60
.eslintrc.js
60
.eslintrc.js
@ -130,6 +130,14 @@ module.exports = {
|
|||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
"@typescript-eslint/no-unused-vars": "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", {
|
"@typescript-eslint/member-delimiter-style": ["error", {
|
||||||
"multiline": {
|
"multiline": {
|
||||||
"delimiter": "semi",
|
"delimiter": "semi",
|
||||||
@ -140,6 +148,28 @@ module.exports = {
|
|||||||
"requireLast": false,
|
"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",
|
"react/display-name": "off",
|
||||||
"space-before-function-paren": "off",
|
"space-before-function-paren": "off",
|
||||||
"@typescript-eslint/space-before-function-paren": ["error", {
|
"@typescript-eslint/space-before-function-paren": ["error", {
|
||||||
@ -218,5 +248,35 @@ module.exports = {
|
|||||||
"@typescript-eslint/consistent-type-imports": "error",
|
"@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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
16
Makefile
16
Makefile
@ -63,6 +63,10 @@ ifeq "$(DETECTED_OS)" "Windows"
|
|||||||
endif
|
endif
|
||||||
yarn run electron-builder --publish onTag $(ELECTRON_BUILDER_EXTRA_ARGS)
|
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)
|
.NOTPARALLEL: $(extension_node_modules)
|
||||||
$(extension_node_modules): node_modules
|
$(extension_node_modules): node_modules
|
||||||
cd $(@:/node_modules=) && ../../node_modules/.bin/npm install --no-audit --no-fund --no-save
|
cd $(@:/node_modules=) && ../../node_modules/.bin/npm install --no-audit --no-fund --no-save
|
||||||
@ -81,19 +85,17 @@ build-extensions: node_modules clean-old-extensions $(extension_dists)
|
|||||||
test-extensions: $(extension_node_modules)
|
test-extensions: $(extension_node_modules)
|
||||||
$(foreach dir, $(extensions), (cd $(dir) && npm run test || exit $?);)
|
$(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__:
|
src/extensions/npm/extensions/__mocks__:
|
||||||
cp -r __mocks__ src/extensions/npm/extensions/
|
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
|
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
|
.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
|
yarn npm:fix-package-version
|
||||||
|
|
||||||
.PHONY: build-extension-types
|
.PHONY: build-extension-types
|
||||||
|
|||||||
@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
import fs from "fs-extra";
|
import fs from "fs-extra";
|
||||||
import path from "path";
|
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 outputCssFile = path.resolve("src/renderer/themes/theme-vars.css");
|
||||||
|
|
||||||
const banner = `/*
|
const banner = `/*
|
||||||
Generated Lens theme CSS-variables, don't edit manually.
|
Generated Lens theme CSS-variables, don't edit manually.
|
||||||
To refresh file run $: yarn run ts-node build/${path.basename(__filename)}
|
To refresh file run $: yarn run ts-node build/${path.basename(__filename)}
|
||||||
*/`;
|
*/`;
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import packageInfo from "../package.json";
|
import packageInfo from "../package.json";
|
||||||
import { type WriteStream } from "fs";
|
|
||||||
import type { FileHandle } from "fs/promises";
|
import type { FileHandle } from "fs/promises";
|
||||||
import { open } from "fs/promises";
|
import { open } from "fs/promises";
|
||||||
|
import type { WriteStream } from "fs-extra";
|
||||||
import { constants, ensureDir, unlink } from "fs-extra";
|
import { constants, ensureDir, unlink } from "fs-extra";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
@ -17,6 +17,7 @@ import AbortController from "abort-controller";
|
|||||||
import { extract } from "tar-stream";
|
import { extract } from "tar-stream";
|
||||||
import gunzip from "gunzip-maybe";
|
import gunzip from "gunzip-maybe";
|
||||||
import { getBinaryName, normalizedPlatform } from "../src/common/vars";
|
import { getBinaryName, normalizedPlatform } from "../src/common/vars";
|
||||||
|
import { isErrnoException } from "../src/common/utils";
|
||||||
|
|
||||||
const pipeline = promisify(_pipeline);
|
const pipeline = promisify(_pipeline);
|
||||||
|
|
||||||
@ -44,6 +45,10 @@ abstract class BinaryDownloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ensureBinary(): Promise<void> {
|
async ensureBinary(): Promise<void> {
|
||||||
|
if (process.env.LENS_SKIP_DOWNLOAD_BINARIES === "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const stream = await fetch(this.url, {
|
const stream = await fetch(this.url, {
|
||||||
timeout: 15 * 60 * 1000, // 15min
|
timeout: 15 * 60 * 1000, // 15min
|
||||||
@ -51,7 +56,7 @@ abstract class BinaryDownloader {
|
|||||||
});
|
});
|
||||||
const total = Number(stream.headers.get("content-length"));
|
const total = Number(stream.headers.get("content-length"));
|
||||||
const bar = this.bar;
|
const bar = this.bar;
|
||||||
let fileHandle: FileHandle;
|
let fileHandle: FileHandle | undefined = undefined;
|
||||||
|
|
||||||
if (isNaN(total)) {
|
if (isNaN(total)) {
|
||||||
throw new Error("no content-length header was present");
|
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" })`
|
* This is necessary because for some reason `createWriteStream({ flags: "wx" })`
|
||||||
* was throwing someplace else and not here
|
* 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(
|
await pipeline(
|
||||||
stream.body,
|
stream.body,
|
||||||
@ -79,7 +84,7 @@ abstract class BinaryDownloader {
|
|||||||
}),
|
}),
|
||||||
...this.getTransformStreams(new Writable({
|
...this.getTransformStreams(new Writable({
|
||||||
write(chunk, encoding, cb) {
|
write(chunk, encoding, cb) {
|
||||||
fileHandle.write(chunk)
|
handle.write(chunk)
|
||||||
.then(() => cb())
|
.then(() => cb())
|
||||||
.catch(cb);
|
.catch(cb);
|
||||||
},
|
},
|
||||||
@ -90,7 +95,7 @@ abstract class BinaryDownloader {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
await fileHandle?.close();
|
await fileHandle?.close();
|
||||||
|
|
||||||
if (error.code === "EEXIST") {
|
if (isErrnoException(error) && error.code === "EEXIST") {
|
||||||
bar.increment(total); // mark as finished
|
bar.increment(total); // mark as finished
|
||||||
controller.abort(); // stop trying to download
|
controller.abort(); // stop trying to download
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
6
build/tsconfig.json
Normal file
6
build/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"./**/*",
|
||||||
|
]
|
||||||
|
}
|
||||||
2374
extensions/kube-object-event-status/package-lock.json
generated
2374
extensions/kube-object-event-status/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
2380
extensions/metrics-cluster-feature/package-lock.json
generated
2380
extensions/metrics-cluster-feature/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -208,14 +208,14 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
|
|||||||
<section>
|
<section>
|
||||||
<SubTitle title="Prometheus" />
|
<SubTitle title="Prometheus" />
|
||||||
<FormSwitch
|
<FormSwitch
|
||||||
control={
|
control={(
|
||||||
<Switcher
|
<Switcher
|
||||||
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
|
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
|
||||||
checked={!!this.featureStates.prometheus && this.props.cluster.status.phase == "connected"}
|
checked={!!this.featureStates.prometheus && this.props.cluster.status.phase == "connected"}
|
||||||
onChange={v => this.togglePrometheus(v.target.checked)}
|
onChange={v => this.togglePrometheus(v.target.checked)}
|
||||||
name="prometheus"
|
name="prometheus"
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
label="Enable bundled Prometheus metrics stack"
|
label="Enable bundled Prometheus metrics stack"
|
||||||
/>
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
@ -226,14 +226,14 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
|
|||||||
<section>
|
<section>
|
||||||
<SubTitle title="Kube State Metrics" />
|
<SubTitle title="Kube State Metrics" />
|
||||||
<FormSwitch
|
<FormSwitch
|
||||||
control={
|
control={(
|
||||||
<Switcher
|
<Switcher
|
||||||
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
|
disabled={this.featureStates.kubeStateMetrics === undefined || !this.isTogglable}
|
||||||
checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.phase == "connected"}
|
checked={!!this.featureStates.kubeStateMetrics && this.props.cluster.status.phase == "connected"}
|
||||||
onChange={v => this.toggleKubeStateMetrics(v.target.checked)}
|
onChange={v => this.toggleKubeStateMetrics(v.target.checked)}
|
||||||
name="node-exporter"
|
name="node-exporter"
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
label="Enable bundled kube-state-metrics stack"
|
label="Enable bundled kube-state-metrics stack"
|
||||||
/>
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
@ -245,14 +245,14 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
|
|||||||
<section>
|
<section>
|
||||||
<SubTitle title="Node Exporter" />
|
<SubTitle title="Node Exporter" />
|
||||||
<FormSwitch
|
<FormSwitch
|
||||||
control={
|
control={(
|
||||||
<Switcher
|
<Switcher
|
||||||
disabled={this.featureStates.nodeExporter === undefined || !this.isTogglable}
|
disabled={this.featureStates.nodeExporter === undefined || !this.isTogglable}
|
||||||
checked={!!this.featureStates.nodeExporter && this.props.cluster.status.phase == "connected"}
|
checked={!!this.featureStates.nodeExporter && this.props.cluster.status.phase == "connected"}
|
||||||
onChange={v => this.toggleNodeExporter(v.target.checked)}
|
onChange={v => this.toggleNodeExporter(v.target.checked)}
|
||||||
name="node-exporter"
|
name="node-exporter"
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
label="Enable bundled node-exporter stack"
|
label="Enable bundled node-exporter stack"
|
||||||
/>
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
@ -271,9 +271,11 @@ export class MetricsSettings extends React.Component<MetricsSettingsProps> {
|
|||||||
className="w-60 h-14"
|
className="w-60 h-14"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{this.canUpgrade && (<small className="hint">
|
{this.canUpgrade && (
|
||||||
An update is available for enabled metrics components.
|
<small className="hint">
|
||||||
</small>)}
|
An update is available for enabled metrics components.
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
2374
extensions/node-menu/package-lock.json
generated
2374
extensions/node-menu/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -68,7 +68,9 @@ export function NodeMenu(props: NodeMenuProps) {
|
|||||||
labelOk: `Drain Node`,
|
labelOk: `Drain Node`,
|
||||||
message: (
|
message: (
|
||||||
<p>
|
<p>
|
||||||
Are you sure you want to drain <b>{nodeName}</b>?
|
{"Are you sure you want to drain "}
|
||||||
|
<b>{nodeName}</b>
|
||||||
|
?
|
||||||
</p>
|
</p>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@ -77,26 +79,42 @@ export function NodeMenu(props: NodeMenuProps) {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MenuItem onClick={shell}>
|
<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>
|
<span className="title">Shell</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{
|
{
|
||||||
node.isUnschedulable()
|
node.isUnschedulable()
|
||||||
? (
|
? (
|
||||||
<MenuItem onClick={unCordon}>
|
<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>
|
<span className="title">Uncordon</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<MenuItem onClick={cordon}>
|
<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>
|
<span className="title">Cordon</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
<MenuItem onClick={drain}>
|
<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>
|
<span className="title">Drain</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</>
|
</>
|
||||||
|
|||||||
2367
extensions/pod-menu/package-lock.json
generated
2367
extensions/pod-menu/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -71,7 +71,11 @@ export class PodAttachMenu extends React.Component<PodAttachMenuProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem onClick={Util.prevDefault(() => this.attachToPod(containers[0].name))}>
|
<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>
|
<span className="title">Attach Pod</span>
|
||||||
{containers.length > 1 && (
|
{containers.length > 1 && (
|
||||||
<>
|
<>
|
||||||
@ -82,7 +86,11 @@ export class PodAttachMenu extends React.Component<PodAttachMenuProps> {
|
|||||||
const { name } = container;
|
const { name } = container;
|
||||||
|
|
||||||
return (
|
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/>
|
<StatusBrick/>
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@ -46,7 +46,11 @@ export class PodLogsMenu extends React.Component<PodLogsMenuProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem onClick={Util.prevDefault(() => this.showLogs(containers[0]))}>
|
<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>
|
<span className="title">Logs</span>
|
||||||
{containers.length > 1 && (
|
{containers.length > 1 && (
|
||||||
<>
|
<>
|
||||||
@ -63,7 +67,11 @@ export class PodLogsMenu extends React.Component<PodLogsMenuProps> {
|
|||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
return (
|
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}
|
{brick}
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@ -79,7 +79,11 @@ export class PodShellMenu extends React.Component<PodShellMenuProps> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem onClick={Util.prevDefault(() => this.execShell(containers[0].name))}>
|
<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>
|
<span className="title">Shell</span>
|
||||||
{containers.length > 1 && (
|
{containers.length > 1 && (
|
||||||
<>
|
<>
|
||||||
@ -90,7 +94,11 @@ export class PodShellMenu extends React.Component<PodShellMenuProps> {
|
|||||||
const { name } = container;
|
const { name } = container;
|
||||||
|
|
||||||
return (
|
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/>
|
<StatusBrick/>
|
||||||
<span>{name}</span>
|
<span>{name}</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@ -47,7 +47,6 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
|
|||||||
|
|
||||||
it(
|
it(
|
||||||
"should navigate around common cluster pages",
|
"should navigate around common cluster pages",
|
||||||
|
|
||||||
async () => {
|
async () => {
|
||||||
const scenariosByParent = pipeline(
|
const scenariosByParent = pipeline(
|
||||||
scenarios,
|
scenarios,
|
||||||
@ -139,7 +138,7 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it(
|
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 () => {
|
async () => {
|
||||||
await navigateToNamespaces(frame);
|
await navigateToNamespaces(frame);
|
||||||
await frame.click("button.add-button");
|
await frame.click("button.add-button");
|
||||||
@ -209,6 +208,10 @@ utils.describeIf(minikubeReady(TEST_NAMESPACE))("Minikube based tests", () => {
|
|||||||
|
|
||||||
await frame.click(".Dock .Button >> text='Create'");
|
await frame.click(".Dock .Button >> text='Create'");
|
||||||
await frame.waitForSelector(`.TableCell >> text=${testPodName}`);
|
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,
|
10 * 60 * 1000,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,9 +24,9 @@ describe("Lens command palette", () => {
|
|||||||
utils.itIf(!isWindows)("opens command dialog from menu", async () => {
|
utils.itIf(!isWindows)("opens command dialog from menu", async () => {
|
||||||
await app.evaluate(async ({ app }) => {
|
await app.evaluate(async ({ app }) => {
|
||||||
await app.applicationMenu
|
await app.applicationMenu
|
||||||
.getMenuItemById("view")
|
?.getMenuItemById("view")
|
||||||
.submenu.getMenuItemById("command-palette")
|
?.submenu?.getMenuItemById("command-palette")
|
||||||
.click();
|
?.click();
|
||||||
});
|
});
|
||||||
await window.waitForSelector(".Select__option >> text=Hotbar: Switch");
|
await window.waitForSelector(".Select__option >> text=Hotbar: Switch");
|
||||||
}, 10*60*1000);
|
}, 10*60*1000);
|
||||||
|
|||||||
@ -108,6 +108,10 @@ export async function lauchMinikubeClusterFromCatalog(window: Page): Promise<Fra
|
|||||||
|
|
||||||
const frame = await minikubeFrame.contentFrame();
|
const frame = await minikubeFrame.contentFrame();
|
||||||
|
|
||||||
|
if (!frame) {
|
||||||
|
throw new Error("No iframe for minikube found");
|
||||||
|
}
|
||||||
|
|
||||||
await frame.waitForSelector("[data-testid=cluster-sidebar]");
|
await frame.waitForSelector("[data-testid=cluster-sidebar]");
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
|
|||||||
6
integration/tsconfig.json
Normal file
6
integration/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"include": [
|
||||||
|
"./**/*",
|
||||||
|
]
|
||||||
|
}
|
||||||
15
package.json
15
package.json
@ -21,7 +21,7 @@
|
|||||||
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
"compile": "env NODE_ENV=production concurrently yarn:compile:*",
|
||||||
"compile:main": "yarn run webpack --config webpack/main.ts",
|
"compile:main": "yarn run webpack --config webpack/main.ts",
|
||||||
"compile:renderer": "yarn run webpack --config webpack/renderer.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-build-version": "yarn run ts-node build/set_build_version.ts",
|
||||||
"npm:fix-package-version": "yarn run ts-node build/set_npm_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:linux": "yarn run compile && electron-builder --linux --dir",
|
||||||
@ -203,6 +203,7 @@
|
|||||||
"@hapi/call": "^8.0.1",
|
"@hapi/call": "^8.0.1",
|
||||||
"@hapi/subtext": "^7.0.3",
|
"@hapi/subtext": "^7.0.3",
|
||||||
"@kubernetes/client-node": "^0.16.3",
|
"@kubernetes/client-node": "^0.16.3",
|
||||||
|
"@material-ui/styles": "^4.11.5",
|
||||||
"@ogre-tools/fp": "5.2.0",
|
"@ogre-tools/fp": "5.2.0",
|
||||||
"@ogre-tools/injectable": "5.2.0",
|
"@ogre-tools/injectable": "5.2.0",
|
||||||
"@ogre-tools/injectable-react": "5.2.0",
|
"@ogre-tools/injectable-react": "5.2.0",
|
||||||
@ -265,13 +266,14 @@
|
|||||||
"tar": "^6.1.11",
|
"tar": "^6.1.11",
|
||||||
"tcp-port-used": "^1.0.2",
|
"tcp-port-used": "^1.0.2",
|
||||||
"tempy": "1.0.1",
|
"tempy": "1.0.1",
|
||||||
|
"typed-regex": "^0.0.8",
|
||||||
"url-parse": "^1.5.10",
|
"url-parse": "^1.5.10",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"win-ca": "^3.5.0",
|
"win-ca": "^3.5.0",
|
||||||
"winston": "^3.7.2",
|
"winston": "^3.7.2",
|
||||||
"winston-console-format": "^1.0.8",
|
"winston-console-format": "^1.0.8",
|
||||||
"winston-transport-browserconsole": "^1.0.5",
|
"winston-transport-browserconsole": "^1.0.5",
|
||||||
"ws": "^7.5.7"
|
"ws": "^8.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@async-fn/jest": "1.5.3",
|
"@async-fn/jest": "1.5.3",
|
||||||
@ -280,11 +282,13 @@
|
|||||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
|
||||||
"@sentry/types": "^6.19.7",
|
"@sentry/types": "^6.19.7",
|
||||||
|
"@testing-library/dom": "^7.31.2",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^12.1.5",
|
"@testing-library/react": "^12.1.5",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/byline": "^4.2.33",
|
"@types/byline": "^4.2.33",
|
||||||
"@types/chart.js": "^2.9.36",
|
"@types/chart.js": "^2.9.36",
|
||||||
|
"@types/circular-dependency-plugin": "5.0.5",
|
||||||
"@types/cli-progress": "^3.9.2",
|
"@types/cli-progress": "^3.9.2",
|
||||||
"@types/color": "^3.0.3",
|
"@types/color": "^3.0.3",
|
||||||
"@types/command-line-args": "^5.2.0",
|
"@types/command-line-args": "^5.2.0",
|
||||||
@ -294,7 +298,6 @@
|
|||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/glob-to-regexp": "^0.4.1",
|
"@types/glob-to-regexp": "^0.4.1",
|
||||||
"@types/gunzip-maybe": "^1.4.0",
|
"@types/gunzip-maybe": "^1.4.0",
|
||||||
"@types/hoist-non-react-statics": "^3.3.1",
|
|
||||||
"@types/html-webpack-plugin": "^3.2.6",
|
"@types/html-webpack-plugin": "^3.2.6",
|
||||||
"@types/http-proxy": "^1.17.9",
|
"@types/http-proxy": "^1.17.9",
|
||||||
"@types/jest": "^26.0.24",
|
"@types/jest": "^26.0.24",
|
||||||
@ -310,9 +313,10 @@
|
|||||||
"@types/npm": "^2.0.32",
|
"@types/npm": "^2.0.32",
|
||||||
"@types/proper-lockfile": "^4.1.2",
|
"@types/proper-lockfile": "^4.1.2",
|
||||||
"@types/randomcolor": "^0.5.6",
|
"@types/randomcolor": "^0.5.6",
|
||||||
"@types/react": "^17.0.44",
|
"@types/react": "^17.0.45",
|
||||||
"@types/react-beautiful-dnd": "^13.1.2",
|
"@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-router-dom": "^5.3.3",
|
||||||
"@types/react-table": "^7.7.11",
|
"@types/react-table": "^7.7.11",
|
||||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||||
@ -360,7 +364,6 @@
|
|||||||
"flex.box": "^3.4.4",
|
"flex.box": "^3.4.4",
|
||||||
"fork-ts-checker-webpack-plugin": "^6.5.0",
|
"fork-ts-checker-webpack-plugin": "^6.5.0",
|
||||||
"gunzip-maybe": "^1.4.2",
|
"gunzip-maybe": "^1.4.2",
|
||||||
"hoist-non-react-statics": "^3.3.2",
|
|
||||||
"html-webpack-plugin": "^5.5.0",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"ignore-loader": "^0.1.2",
|
"ignore-loader": "^0.1.2",
|
||||||
|
|||||||
@ -19,7 +19,7 @@ exports[`add-cluster - navigation using application menu when navigating to add
|
|||||||
Add Clusters from Kubeconfig
|
Add Clusters from Kubeconfig
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Clusters added here are
|
Clusters added here are
|
||||||
<b>
|
<b>
|
||||||
not
|
not
|
||||||
</b>
|
</b>
|
||||||
@ -27,16 +27,14 @@ exports[`add-cluster - navigation using application menu when navigating to add
|
|||||||
<code>
|
<code>
|
||||||
~/.kube/config
|
~/.kube/config
|
||||||
</code>
|
</code>
|
||||||
file.
|
file.
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://docs.k8slens.dev/main//catalog/add-clusters/"
|
href="https://docs.k8slens.dev/main//catalog/add-clusters/"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Read more about adding clusters
|
Read more about adding clusters.
|
||||||
</a>
|
</a>
|
||||||
.
|
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="flex column"
|
class="flex column"
|
||||||
@ -7,10 +7,18 @@ import type { RenderResult } from "@testing-library/react";
|
|||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } 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 isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
// TODO: Make components free of side effects by making them deterministic
|
// TODO: Make components free of side effects by making them deterministic
|
||||||
jest.mock("../../renderer/components/tooltip");
|
jest.mock("../../renderer/components/tooltip/tooltip", () => ({
|
||||||
jest.mock("../../renderer/components/monaco-editor/monaco-editor");
|
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", () => {
|
describe("add-cluster - navigation using application menu", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -261,14 +261,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center expandable"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -557,14 +557,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center expandable"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -853,14 +853,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center expandable"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -887,15 +887,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="false"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-child-id"
|
data-test-id="some-extension-name-some-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -907,15 +907,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="false"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-other-child-id"
|
data-test-id="some-extension-name-some-other-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1193,15 +1193,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
class="nav-item flex gaps align-center expandable active"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -1228,16 +1228,16 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="true"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-child-id"
|
data-test-id="some-extension-name-some-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
class="nav-item flex gaps align-center active"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1249,15 +1249,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="false"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-other-child-id"
|
data-test-id="some-extension-name-some-other-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1281,7 +1281,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<div
|
<div
|
||||||
class="Tab flex gaps align-center active"
|
class="Tab flex gaps align-center active"
|
||||||
data-is-active-test="true"
|
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"
|
role="tab"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
@ -1294,7 +1294,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<div
|
<div
|
||||||
class="Tab flex gaps align-center"
|
class="Tab flex gaps align-center"
|
||||||
data-is-active-test="false"
|
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"
|
role="tab"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
@ -1577,15 +1577,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
class="nav-item flex gaps align-center expandable active"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -1612,15 +1612,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="false"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-child-id"
|
data-test-id="some-extension-name-some-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1632,16 +1632,16 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="true"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-other-child-id"
|
data-test-id="some-extension-name-some-other-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
class="nav-item flex gaps align-center active"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1665,7 +1665,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<div
|
<div
|
||||||
class="Tab flex gaps align-center"
|
class="Tab flex gaps align-center"
|
||||||
data-is-active-test="false"
|
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"
|
role="tab"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
@ -1678,7 +1678,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<div
|
<div
|
||||||
class="Tab flex gaps align-center active"
|
class="Tab flex gaps align-center active"
|
||||||
data-is-active-test="true"
|
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"
|
role="tab"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
@ -1961,15 +1961,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
class="nav-item flex gaps align-center expandable active"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -2004,7 +2004,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<div
|
<div
|
||||||
class="Tab flex gaps align-center active"
|
class="Tab flex gaps align-center active"
|
||||||
data-is-active-test="true"
|
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"
|
role="tab"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
@ -2017,7 +2017,7 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
<div
|
<div
|
||||||
class="Tab flex gaps align-center"
|
class="Tab flex gaps align-center"
|
||||||
data-is-active-test="false"
|
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"
|
role="tab"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
@ -2300,14 +2300,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center expandable"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
@ -2334,15 +2334,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="false"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-child-id"
|
data-test-id="some-extension-name-some-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -2354,15 +2354,15 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-is-active-test="false"
|
||||||
data-parent-id-test="some-extension-id-some-parent-id"
|
data-parent-id-test="some-extension-name-some-parent-id"
|
||||||
data-test-id="some-extension-id-some-other-child-id"
|
data-test-id="some-extension-name-some-other-child-id"
|
||||||
data-testid="sidebar-item"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -2640,14 +2640,14 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="SidebarItem"
|
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-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"
|
data-testid="sidebar-item"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="nav-item flex gaps align-center expandable"
|
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="/"
|
href="/"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -21,6 +21,8 @@ import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable"
|
|||||||
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
import pathExistsInjectable from "../../common/fs/path-exists.injectable";
|
||||||
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
import readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||||
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
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", () => {
|
describe("cluster - sidebar and tab navigation for core", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -34,7 +36,6 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
rendererDi = applicationBuilder.dis.rendererDi;
|
rendererDi = applicationBuilder.dis.rendererDi;
|
||||||
|
|
||||||
applicationBuilder.setEnvironmentToClusterFrame();
|
applicationBuilder.setEnvironmentToClusterFrame();
|
||||||
|
|
||||||
applicationBuilder.beforeSetups(({ rendererDi }) => {
|
applicationBuilder.beforeSetups(({ rendererDi }) => {
|
||||||
rendererDi.override(
|
rendererDi.override(
|
||||||
directoryForLensLocalStorageInjectable,
|
directoryForLensLocalStorageInjectable,
|
||||||
@ -72,13 +73,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent is highlighted", () => {
|
it("parent is highlighted", () => {
|
||||||
const parent = getSidebarItem(rendered, "some-parent-id");
|
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", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
const child = getSidebarItem(rendered, "some-child-id");
|
||||||
|
|
||||||
expect(child).toBe(null);
|
expect(child).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("child page is shown", () => {
|
it("child page is shown", () => {
|
||||||
@ -101,6 +102,11 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
applicationBuilder.beforeRender(async ({ rendererDi }) => {
|
||||||
|
const sidebarStorage = rendererDi.inject(sidebarStorageInjectable);
|
||||||
|
|
||||||
|
await sidebarStorage.whenReady;
|
||||||
|
});
|
||||||
|
|
||||||
rendered = await applicationBuilder.render();
|
rendered = await applicationBuilder.render();
|
||||||
});
|
});
|
||||||
@ -112,13 +118,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent sidebar item is not highlighted", () => {
|
it("parent sidebar item is not highlighted", () => {
|
||||||
const parent = getSidebarItem(rendered, "some-parent-id");
|
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", () => {
|
it("parent sidebar item is expanded", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
const child = getSidebarItem(rendered, "some-child-id");
|
||||||
|
|
||||||
expect(child).not.toBe(null);
|
expect(child).not.toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -148,7 +154,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent sidebar item is not expanded", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
const child = getSidebarItem(rendered, "some-child-id");
|
||||||
|
|
||||||
expect(child).toBe(null);
|
expect(child).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -175,7 +181,7 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent sidebar item is not expanded", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
const child = getSidebarItem(rendered, "some-child-id");
|
||||||
|
|
||||||
expect(child).toBe(null);
|
expect(child).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -191,13 +197,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent sidebar item is not highlighted", () => {
|
it("parent sidebar item is not highlighted", () => {
|
||||||
const parent = getSidebarItem(rendered, "some-parent-id");
|
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", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
const child = getSidebarItem(rendered, "some-child-id");
|
||||||
|
|
||||||
expect(child).toBe(null);
|
expect(child).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when a parent sidebar item is expanded", () => {
|
describe("when a parent sidebar item is expanded", () => {
|
||||||
@ -216,13 +222,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent sidebar item is not highlighted", () => {
|
it("parent sidebar item is not highlighted", () => {
|
||||||
const parent = getSidebarItem(rendered, "some-parent-id");
|
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", () => {
|
it("parent sidebar item is expanded", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
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", () => {
|
describe("when a child of the parent is selected", () => {
|
||||||
@ -241,13 +247,13 @@ describe("cluster - sidebar and tab navigation for core", () => {
|
|||||||
it("parent is highlighted", () => {
|
it("parent is highlighted", () => {
|
||||||
const parent = getSidebarItem(rendered, "some-parent-id");
|
const parent = getSidebarItem(rendered, "some-parent-id");
|
||||||
|
|
||||||
expect(parent.dataset.isActiveTest).toBe("true");
|
expect(parent?.dataset.isActiveTest).toBe("true");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("child is highlighted", () => {
|
it("child is highlighted", () => {
|
||||||
const child = getSidebarItem(rendered, "some-child-id");
|
const child = getSidebarItem(rendered, "some-child-id");
|
||||||
|
|
||||||
expect(child.dataset.isActiveTest).toBe("true");
|
expect(child?.dataset.isActiveTest).toBe("true");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("child page is shown", () => {
|
it("child page is shown", () => {
|
||||||
@ -288,11 +294,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({
|
const testSidebarItemsInjectable = getInjectable({
|
||||||
id: "some-sidebar-items-injectable",
|
id: "some-sidebar-items-injectable",
|
||||||
|
|
||||||
@ -325,7 +326,7 @@ const testSidebarItemsInjectable = getInjectable({
|
|||||||
injectionToken: sidebarItemsInjectionToken,
|
injectionToken: sidebarItemsInjectionToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
const testRouteInjectable = getInjectable({
|
const testRouteInjectable = getInjectable({
|
||||||
id: "some-route-injectable-id",
|
id: "some-route-injectable-id",
|
||||||
|
|
||||||
instantiate: () => ({
|
instantiate: () => ({
|
||||||
|
|||||||
@ -5,8 +5,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import { fireEvent } 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 directoryForLensLocalStorageInjectable from "../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
|
||||||
import routesInjectable from "../../renderer/routes/routes.injectable";
|
import routesInjectable from "../../renderer/routes/routes.injectable";
|
||||||
import { matches } from "lodash/fp";
|
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 readJsonFileInjectable from "../../common/fs/read-json-file.injectable";
|
||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
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", () => {
|
describe("cluster - sidebar and tab navigation for extensions", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -41,9 +43,8 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
|
|
||||||
describe("given extension with cluster pages and cluster page menus", () => {
|
describe("given extension with cluster pages and cluster page menus", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const testExtension = getRendererExtensionFake(
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
extensionStubWithSidebarItems,
|
const testExtension = getRendererExtensionFake(extensionStubWithSidebarItems);
|
||||||
);
|
|
||||||
|
|
||||||
await applicationBuilder.addExtensions(testExtension);
|
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", () => {
|
describe("given no state for expanded sidebar items exists, and navigated to child sidebar item, when rendered", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
applicationBuilder.beforeRender(({ rendererDi }) => {
|
applicationBuilder.beforeRender(({ rendererDi }) => {
|
||||||
const navigateToRoute = rendererDi.inject(
|
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
|
||||||
navigateToRouteInjectionToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
const route = rendererDi
|
const route = rendererDi
|
||||||
.inject(routesInjectable)
|
.inject(routesInjectable)
|
||||||
.get()
|
.get()
|
||||||
.find(
|
.find(
|
||||||
matches({
|
matches({
|
||||||
path: "/extension/some-extension-id/some-child-page-id",
|
path: "/extension/some-extension-name/some-child-page-id",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert(route);
|
||||||
navigateToRoute(route);
|
navigateToRoute(route);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -77,19 +76,19 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
it("parent is highlighted", () => {
|
it("parent is highlighted", () => {
|
||||||
const parent = getSidebarItem(
|
const parent = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
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",
|
"/some-directory-for-lens-local-storage/app.json",
|
||||||
{
|
{
|
||||||
sidebar: {
|
sidebar: {
|
||||||
expanded: { "some-extension-id-some-parent-id": true },
|
expanded: { "some-extension-name-some-parent-id": true },
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -123,19 +122,19 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
it("parent sidebar item is not highlighted", () => {
|
it("parent sidebar item is not highlighted", () => {
|
||||||
const parent = getSidebarItem(
|
const parent = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("parent sidebar item is expanded", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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",
|
"/some-directory-for-lens-local-storage/app.json",
|
||||||
{
|
{
|
||||||
sidebar: {
|
sidebar: {
|
||||||
expanded: { "some-extension-id-some-unknown-parent-id": true },
|
expanded: { "some-extension-name-some-unknown-parent-id": true },
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -165,10 +164,10 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
it("parent sidebar item is not expanded", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("parent sidebar item is not highlighted", () => {
|
||||||
const parent = getSidebarItem(
|
const parent = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("parent sidebar item is not expanded", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
describe("when a parent sidebar item is expanded", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const parentLink = rendered.getByTestId(
|
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);
|
fireEvent.click(parentLink);
|
||||||
@ -245,25 +244,25 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
it("parent sidebar item is not highlighted", () => {
|
it("parent sidebar item is not highlighted", () => {
|
||||||
const parent = getSidebarItem(
|
const parent = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("parent sidebar item is expanded", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
describe("when a child of the parent is selected", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const childLink = rendered.getByTestId(
|
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);
|
fireEvent.click(childLink);
|
||||||
@ -276,19 +275,19 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
it("parent is highlighted", () => {
|
it("parent is highlighted", () => {
|
||||||
const parent = getSidebarItem(
|
const parent = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("child is highlighted", () => {
|
||||||
const child = getSidebarItem(
|
const child = getSidebarItem(
|
||||||
rendered,
|
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", () => {
|
it("child page is shown", () => {
|
||||||
@ -301,7 +300,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
|
|
||||||
it("tab for child page is active", () => {
|
it("tab for child page is active", () => {
|
||||||
const tabLink = rendered.getByTestId(
|
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");
|
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", () => {
|
it("tab for sibling page is not active", () => {
|
||||||
const tabLink = rendered.getByTestId(
|
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");
|
expect(tabLink.dataset.isActiveTest).toBe("false");
|
||||||
@ -338,7 +337,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
|
|
||||||
expect(actual).toEqual({
|
expect(actual).toEqual({
|
||||||
sidebar: {
|
sidebar: {
|
||||||
expanded: { "some-extension-id-some-parent-id": true },
|
expanded: { "some-extension-name-some-parent-id": true },
|
||||||
width: 200,
|
width: 200,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -347,7 +346,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
describe("when selecting sibling tab", () => {
|
describe("when selecting sibling tab", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const childTabLink = rendered.getByTestId(
|
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);
|
fireEvent.click(childTabLink);
|
||||||
@ -365,7 +364,7 @@ describe("cluster - sidebar and tab navigation for extensions", () => {
|
|||||||
|
|
||||||
it("tab for sibling page is active", () => {
|
it("tab for sibling page is active", () => {
|
||||||
const tabLink = rendered.getByTestId(
|
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");
|
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", () => {
|
it("tab for previous page is not active", () => {
|
||||||
const tabLink = rendered.getByTestId(
|
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");
|
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",
|
id: "some-extension-id",
|
||||||
|
name: "some-extension-name",
|
||||||
clusterPages: [
|
clusterPages: [
|
||||||
{
|
{
|
||||||
components: {
|
components: {
|
||||||
@ -396,7 +395,6 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "some-child-page-id",
|
id: "some-child-page-id",
|
||||||
|
|
||||||
@ -404,7 +402,6 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
|
|||||||
Page: () => <div data-testid="some-child-page">Some child page</div>,
|
Page: () => <div data-testid="some-child-page">Some child page</div>,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: "some-other-child-page-id",
|
id: "some-other-child-page-id",
|
||||||
|
|
||||||
@ -415,7 +412,6 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
clusterPageMenus: [
|
clusterPageMenus: [
|
||||||
{
|
{
|
||||||
id: "some-parent-id",
|
id: "some-parent-id",
|
||||||
@ -433,7 +429,7 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
|
|||||||
title: "Child 1",
|
title: "Child 1",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Icon: null,
|
Icon: null as never,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -444,13 +440,8 @@ const extensionStubWithSidebarItems: Partial<LensRendererExtension> = {
|
|||||||
title: "Child 2",
|
title: "Child 2",
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
Icon: null,
|
Icon: null as never,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSidebarItem = (rendered: RenderResult, itemId: string) =>
|
|
||||||
rendered
|
|
||||||
.queryAllByTestId("sidebar-item")
|
|
||||||
.find((x) => x.dataset.idTest === itemId) || null;
|
|
||||||
|
|||||||
@ -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 type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } 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 { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
||||||
|
import { getSidebarItem } from "../utils";
|
||||||
|
|
||||||
describe("cluster - visibility of sidebar items", () => {
|
describe("cluster - visibility of sidebar items", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -43,7 +44,7 @@ describe("cluster - visibility of sidebar items", () => {
|
|||||||
it("related sidebar item does not exist", () => {
|
it("related sidebar item does not exist", () => {
|
||||||
const item = getSidebarItem(rendered, "some-item-id");
|
const item = getSidebarItem(rendered, "some-item-id");
|
||||||
|
|
||||||
expect(item).toBeNull();
|
expect(item).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when kube resource becomes allowed", () => {
|
describe("when kube resource becomes allowed", () => {
|
||||||
@ -58,17 +59,12 @@ describe("cluster - visibility of sidebar items", () => {
|
|||||||
it("related sidebar item exists", () => {
|
it("related sidebar item exists", () => {
|
||||||
const item = getSidebarItem(rendered, "some-item-id");
|
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({
|
const testRouteInjectable = getInjectable({
|
||||||
id: "some-route-injectable-id",
|
id: "some-route-injectable-id",
|
||||||
|
|
||||||
|
|||||||
@ -2,12 +2,11 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* 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 type { FakeExtensionData, TestExtension } from "../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
import { getRendererExtensionFake } 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 React from "react";
|
||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import currentPathInjectable from "../renderer/routes/current-path.injectable";
|
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 type { ApplicationBuilder } from "../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } 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 () => {
|
beforeEach(async () => {
|
||||||
applicationBuilder = getApplicationBuilder();
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
|
|
||||||
testExtension = getRendererExtensionFake(
|
testExtension = getRendererExtensionFake(
|
||||||
extensionWithPagesHavingSpecialCharacters,
|
extensionWithPagesHavingSpecialCharacters,
|
||||||
@ -44,14 +44,14 @@ describe("extension special characters in page registrations", () => {
|
|||||||
it("knows URL", () => {
|
it("knows URL", () => {
|
||||||
const currentPath = applicationBuilder.dis.rendererDi.inject(currentPathInjectable);
|
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> = {
|
const extensionWithPagesHavingSpecialCharacters: FakeExtensionData = {
|
||||||
id: "@some-extension-id/",
|
id: "some-extension-id",
|
||||||
|
name: "@some-extension-name/",
|
||||||
globalPages: [
|
globalPages: [
|
||||||
{
|
{
|
||||||
id: "/some-page-id/",
|
id: "/some-page-id/",
|
||||||
|
|||||||
@ -23,9 +23,7 @@ exports[`extensions - navigation using application menu when navigating to exten
|
|||||||
class="notice mb-14 mt-3"
|
class="notice mb-14 mt-3"
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
Add new features via Lens Extensions.
|
Add new features via Lens Extensions. Check out the
|
||||||
|
|
||||||
Check out
|
|
||||||
<a
|
<a
|
||||||
href="https://docs.k8slens.dev/main//extensions/"
|
href="https://docs.k8slens.dev/main//extensions/"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
@ -33,8 +31,7 @@ exports[`extensions - navigation using application menu when navigating to exten
|
|||||||
>
|
>
|
||||||
docs
|
docs
|
||||||
</a>
|
</a>
|
||||||
|
and list of
|
||||||
and list of
|
|
||||||
<a
|
<a
|
||||||
href="https://github.com/lensapp/lens-extensions/blob/main/README.md"
|
href="https://github.com/lensapp/lens-extensions/blob/main/README.md"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
|
|||||||
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
||||||
import extensionsStoreInjectable from "../../extensions/extensions-store/extensions-store.injectable";
|
import extensionsStoreInjectable from "../../extensions/extensions-store/extensions-store.injectable";
|
||||||
import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store";
|
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 fileSystemProvisionerStoreInjectable from "../../extensions/extension-loader/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 type { FileSystemProvisionerStore } from "../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store";
|
||||||
import focusWindowInjectable from "../../renderer/ipc-channel-listeners/focus-window.injectable";
|
import focusWindowInjectable from "../../renderer/ipc-channel-listeners/focus-window.injectable";
|
||||||
|
|
||||||
// TODO: Make components free of side effects by making them deterministic
|
// TODO: Make components free of side effects by making them deterministic
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* 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 type { FakeExtensionData, TestExtension } from "../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
import { getRendererExtensionFake } 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 React from "react";
|
||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import { fireEvent } 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 queryParametersInjectable from "../renderer/routes/query-parameters.injectable";
|
||||||
import currentPathInjectable from "../renderer/routes/current-path.injectable";
|
import currentPathInjectable from "../renderer/routes/current-path.injectable";
|
||||||
import type { IComputedValue } from "mobx";
|
import type { IComputedValue } from "mobx";
|
||||||
import type { LensRendererExtension } from "../extensions/lens-renderer-extension";
|
|
||||||
import { getApplicationBuilder } from "../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../renderer/components/test-utils/get-application-builder";
|
||||||
|
|
||||||
describe("navigate to extension page", () => {
|
describe("navigate to extension page", () => {
|
||||||
@ -22,6 +21,7 @@ describe("navigate to extension page", () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const applicationBuilder = getApplicationBuilder();
|
const applicationBuilder = getApplicationBuilder();
|
||||||
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
|
|
||||||
testExtension = getRendererExtensionFake(
|
testExtension = getRendererExtensionFake(
|
||||||
extensionWithPagesHavingParameters,
|
extensionWithPagesHavingParameters,
|
||||||
@ -51,7 +51,7 @@ describe("navigate to extension page", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("URL is correct", () => {
|
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", () => {
|
it("query parameters is empty", () => {
|
||||||
@ -70,7 +70,7 @@ describe("navigate to extension page", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("URL is correct", () => {
|
it("URL is correct", () => {
|
||||||
expect(currentPath.get()).toBe("/extension/some-extension-id");
|
expect(currentPath.get()).toBe("/extension/some-extension-name");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("knows query parameters", () => {
|
it("knows query parameters", () => {
|
||||||
@ -98,7 +98,7 @@ describe("navigate to extension page", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("URL is correct", () => {
|
it("URL is correct", () => {
|
||||||
expect(currentPath.get()).toBe("/extension/some-extension-id");
|
expect(currentPath.get()).toBe("/extension/some-extension-name");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("knows query parameters", () => {
|
it("knows query parameters", () => {
|
||||||
@ -120,14 +120,14 @@ describe("navigate to extension page", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("URL is correct", () => {
|
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",
|
id: "some-extension-id",
|
||||||
|
name: "some-extension-name",
|
||||||
globalPages: [
|
globalPages: [
|
||||||
{
|
{
|
||||||
components: {
|
components: {
|
||||||
@ -159,20 +159,14 @@ const extensionWithPagesHavingParameters: Partial<LensRendererExtension> = {
|
|||||||
|
|
||||||
params: {
|
params: {
|
||||||
someStringParameter: "some-string-value",
|
someStringParameter: "some-string-value",
|
||||||
|
|
||||||
someNumberParameter: {
|
someNumberParameter: {
|
||||||
defaultValue: 42,
|
defaultValue: 42,
|
||||||
|
|
||||||
stringify: (value) => value.toString(),
|
stringify: (value) => value.toString(),
|
||||||
|
|
||||||
parse: (value) => (value ? Number(value) : undefined),
|
parse: (value) => (value ? Number(value) : undefined),
|
||||||
},
|
},
|
||||||
|
|
||||||
someArrayParameter: {
|
someArrayParameter: {
|
||||||
defaultValue: ["some-array-value", "some-other-array-value"],
|
defaultValue: ["some-array-value", "some-other-array-value"],
|
||||||
|
|
||||||
stringify: (value) => value.join(","),
|
stringify: (value) => value.join(","),
|
||||||
|
|
||||||
parse: (value: string[]) => (!isEmpty(value) ? value : undefined),
|
parse: (value: string[]) => (!isEmpty(value) ? value : undefined),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -124,7 +124,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-26-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -140,7 +140,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-26-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -150,7 +150,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-26-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -209,7 +209,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-27-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -225,7 +225,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-27-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -235,7 +235,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-27-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -281,17 +281,12 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -349,7 +344,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-28-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -365,7 +360,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-28-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -375,7 +370,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-28-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -434,7 +429,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-29-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -450,7 +445,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-29-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -460,7 +455,7 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-29-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -827,7 +822,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -843,7 +838,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -853,7 +848,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -912,7 +907,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -928,7 +923,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -938,7 +933,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -984,17 +979,12 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -1052,7 +1042,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -1068,7 +1058,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -1078,7 +1068,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -1137,7 +1127,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -1153,7 +1143,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -1163,7 +1153,7 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -314,7 +314,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -330,7 +330,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -340,7 +340,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -399,7 +399,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -415,7 +415,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -425,7 +425,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -471,17 +471,12 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -539,7 +534,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -555,7 +550,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -565,7 +560,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -624,7 +619,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -640,7 +635,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -650,7 +645,7 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -112,7 +112,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -128,7 +128,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +138,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -197,7 +197,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -213,7 +213,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +223,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -269,17 +269,12 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -337,7 +332,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -353,7 +348,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -363,7 +358,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -422,7 +417,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -438,7 +433,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -448,7 +443,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -668,7 +663,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-14-live-region"
|
id="react-select-minimap-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -684,7 +679,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-14-placeholder"
|
id="react-select-minimap-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -694,7 +689,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-14-placeholder"
|
aria-describedby="react-select-minimap-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -752,7 +747,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-15-live-region"
|
id="react-select-editor-line-numbers-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -768,7 +763,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-15-placeholder"
|
id="react-select-editor-line-numbers-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -778,7 +773,7 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-15-placeholder"
|
aria-describedby="react-select-editor-line-numbers-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -112,7 +112,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -128,7 +128,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +138,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -197,7 +197,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -213,7 +213,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +223,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -269,17 +269,12 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -337,7 +332,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -353,7 +348,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -363,7 +358,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -422,7 +417,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -438,7 +433,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -448,7 +443,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -657,7 +652,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-14-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -673,7 +668,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-14-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -683,7 +678,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-14-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -742,7 +737,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-15-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -758,7 +753,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-15-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -768,7 +763,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-15-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -814,17 +809,12 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -882,7 +872,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-16-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -898,7 +888,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-16-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -908,7 +898,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-16-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -967,7 +957,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-17-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -983,7 +973,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-17-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -993,7 +983,7 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-17-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -112,7 +112,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -128,7 +128,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +138,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -197,7 +197,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -213,7 +213,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +223,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -269,17 +269,12 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -337,7 +332,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -353,7 +348,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -363,7 +358,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -422,7 +417,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -438,7 +433,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -448,7 +443,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -664,7 +659,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-14-live-region"
|
id="react-select-download-mirror-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -680,7 +675,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-14-placeholder"
|
id="react-select-download-mirror-input-placeholder"
|
||||||
>
|
>
|
||||||
Download mirror for kubectl
|
Download mirror for kubectl
|
||||||
</div>
|
</div>
|
||||||
@ -690,7 +685,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-14-placeholder"
|
aria-describedby="react-select-download-mirror-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -845,7 +840,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-15-live-region"
|
id="react-select-HelmRepoSelect-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -861,7 +856,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-15-placeholder"
|
id="react-select-HelmRepoSelect-placeholder"
|
||||||
>
|
>
|
||||||
Repositories
|
Repositories
|
||||||
</div>
|
</div>
|
||||||
@ -871,7 +866,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-15-placeholder"
|
aria-describedby="react-select-HelmRepoSelect-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -112,7 +112,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -128,7 +128,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +138,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -197,7 +197,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -213,7 +213,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +223,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -269,17 +269,12 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -337,7 +332,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -353,7 +348,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -363,7 +358,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -422,7 +417,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -438,7 +433,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -448,7 +443,7 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -300,7 +300,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -316,7 +316,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -326,7 +326,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -385,7 +385,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -401,7 +401,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -411,7 +411,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -457,17 +457,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -525,7 +520,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -541,7 +536,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -551,7 +546,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -610,7 +605,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -626,7 +621,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -636,7 +631,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -845,7 +840,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-14-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -861,7 +856,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-14-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -871,7 +866,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-14-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -930,7 +925,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-15-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -946,7 +941,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-15-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -956,7 +951,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-15-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -1002,17 +997,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -1070,7 +1060,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-16-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -1086,7 +1076,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-16-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -1096,7 +1086,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-16-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -1155,7 +1145,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-17-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -1171,7 +1161,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-17-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -1181,7 +1171,7 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-17-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -112,7 +112,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -128,7 +128,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +138,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -197,7 +197,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -213,7 +213,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +223,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -269,17 +269,12 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -337,7 +332,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -353,7 +348,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -363,7 +358,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -422,7 +417,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -438,7 +433,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -448,7 +443,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -689,7 +684,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-14-live-region"
|
id="react-select-terminal-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -705,7 +700,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-14-placeholder"
|
id="react-select-terminal-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -715,7 +710,7 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-14-placeholder"
|
aria-describedby="react-select-terminal-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -114,7 +114,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-2-live-region"
|
id="react-select-theme-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -130,7 +130,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-2-placeholder"
|
id="react-select-theme-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -140,7 +140,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-2-placeholder"
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -199,7 +199,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-3-live-region"
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -215,7 +215,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-3-placeholder"
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -225,7 +225,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-3-placeholder"
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -271,17 +271,12 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
<p
|
<p
|
||||||
class="mt-4 mb-5 leading-relaxed"
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
>
|
>
|
||||||
This setting is to change the registry URL for installing extensions by name.
|
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
|
||||||
If you are unable to access the default registry (
|
|
||||||
https://registry.npmjs.org
|
|
||||||
)
|
|
||||||
|
|
||||||
you can change it in your
|
|
||||||
<b>
|
<b>
|
||||||
.npmrc
|
.npmrc
|
||||||
</b>
|
</b>
|
||||||
file or in the input below.
|
file or in the input below.
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
class="Input theme round black disabled invalid"
|
class="Input theme round black disabled invalid"
|
||||||
@ -339,7 +334,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-4-live-region"
|
id="react-select-update-channel-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -355,7 +350,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-4-placeholder"
|
id="react-select-update-channel-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -365,7 +360,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-4-placeholder"
|
aria-describedby="react-select-update-channel-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -424,7 +419,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
id="react-select-5-live-region"
|
id="react-select-timezone-input-live-region"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
aria-atomic="false"
|
aria-atomic="false"
|
||||||
@ -440,7 +435,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
id="react-select-5-placeholder"
|
id="react-select-timezone-input-placeholder"
|
||||||
>
|
>
|
||||||
Select...
|
Select...
|
||||||
</div>
|
</div>
|
||||||
@ -450,7 +445,7 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-5-placeholder"
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
|
|||||||
@ -12,8 +12,6 @@ import { routeInjectionToken } from "../../common/front-end-routing/route-inject
|
|||||||
import { computed } from "mobx";
|
import { computed } from "mobx";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
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 { preferenceNavigationItemInjectionToken } from "../../renderer/components/+preferences/preferences-navigation/preference-navigation-items.injectable";
|
||||||
import routeIsActiveInjectable from "../../renderer/routes/route-is-active.injectable";
|
import routeIsActiveInjectable from "../../renderer/routes/route-is-active.injectable";
|
||||||
import { Preferences } from "../../renderer/components/+preferences";
|
import { Preferences } from "../../renderer/components/+preferences";
|
||||||
@ -26,6 +24,7 @@ import { createObservableHistory } from "mobx-observable-history";
|
|||||||
import navigateToPreferenceTabInjectable from "../../renderer/components/+preferences/preferences-navigation/navigate-to-preference-tab.injectable";
|
import navigateToPreferenceTabInjectable from "../../renderer/components/+preferences/preferences-navigation/navigate-to-preference-tab.injectable";
|
||||||
import navigateToFrontPageInjectable from "../../common/front-end-routing/navigate-to-front-page.injectable";
|
import navigateToFrontPageInjectable from "../../common/front-end-routing/navigate-to-front-page.injectable";
|
||||||
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token";
|
||||||
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
|
||||||
describe("preferences - closing-preferences", () => {
|
describe("preferences - closing-preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -45,10 +44,10 @@ describe("preferences - closing-preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
|
|
||||||
rendererDi.override(navigateToFrontPageInjectable, (di) => {
|
rendererDi.override(navigateToFrontPageInjectable, (di) => {
|
||||||
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
|
||||||
|
|||||||
@ -7,10 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } 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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import navigateToProxyPreferencesInjectable from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
import navigateToProxyPreferencesInjectable
|
|
||||||
from "../../common/front-end-routing/routes/preferences/proxy/navigate-to-proxy-preferences.injectable";
|
|
||||||
|
|
||||||
describe("preferences - navigation to application preferences", () => {
|
describe("preferences - navigation to application preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -24,10 +22,10 @@ describe("preferences - navigation to application preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } 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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
|
||||||
|
|
||||||
describe("preferences - navigation to editor preferences", () => {
|
describe("preferences - navigation to editor preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -23,10 +22,10 @@ describe("preferences - navigation to editor preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,11 +7,10 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } 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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
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 React from "react";
|
||||||
import { getRendererExtensionFake } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
import type { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
|
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation to extension specific preferences", () => {
|
describe("preferences - navigation to extension specific preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -25,10 +24,10 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -61,6 +60,7 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
|
|
||||||
describe("when extension with specific preferences is enabled", () => {
|
describe("when extension with specific preferences is enabled", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
const testExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
|
const testExtension = getRendererExtensionFake(extensionStubWithExtensionSpecificPreferenceItems);
|
||||||
|
|
||||||
applicationBuilder.addExtensions(testExtension);
|
applicationBuilder.addExtensions(testExtension);
|
||||||
@ -107,9 +107,9 @@ describe("preferences - navigation to extension specific preferences", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const extensionStubWithExtensionSpecificPreferenceItems: Partial<LensRendererExtension> = {
|
const extensionStubWithExtensionSpecificPreferenceItems: FakeExtensionData = {
|
||||||
id: "some-test-extension-id",
|
id: "some-extension-id",
|
||||||
|
name: "some-extension-name",
|
||||||
appPreferences: [
|
appPreferences: [
|
||||||
{
|
{
|
||||||
title: "Some preference item",
|
title: "Some preference item",
|
||||||
|
|||||||
@ -7,9 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } 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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
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 { observable } from "mobx";
|
||||||
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation to kubernetes preferences", () => {
|
describe("preferences - navigation to kubernetes preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -24,10 +23,10 @@ describe("preferences - navigation to kubernetes preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } 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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
|
||||||
|
|
||||||
describe("preferences - navigation to proxy preferences", () => {
|
describe("preferences - navigation to proxy preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -22,10 +21,10 @@ describe("preferences - navigation to proxy preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,13 +6,13 @@ import type { RenderResult } from "@testing-library/react";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } 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 { FakeExtensionData } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
|
import { getRendererExtensionFakeFor } from "../../renderer/components/test-utils/get-renderer-extension-fake";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
|
||||||
import navigateToTelemetryPreferencesInjectable from "../../common/front-end-routing/routes/preferences/telemetry/navigate-to-telemetry-preferences.injectable";
|
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";
|
import sentryDnsUrlInjectable from "../../renderer/components/+preferences/sentry-dns-url.injectable";
|
||||||
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation to telemetry preferences", () => {
|
describe("preferences - navigation to telemetry preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -26,10 +26,10 @@ describe("preferences - navigation to telemetry preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -62,8 +62,8 @@ describe("preferences - navigation to telemetry preferences", () => {
|
|||||||
|
|
||||||
describe("when extension with telemetry preference items gets enabled", () => {
|
describe("when extension with telemetry preference items gets enabled", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const testExtensionWithTelemetryPreferenceItems =
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
getRendererExtensionFake(extensionStubWithTelemetryPreferenceItems);
|
const testExtensionWithTelemetryPreferenceItems = getRendererExtensionFake(extensionStubWithTelemetryPreferenceItems);
|
||||||
|
|
||||||
applicationBuilder.addExtensions(
|
applicationBuilder.addExtensions(
|
||||||
testExtensionWithTelemetryPreferenceItems,
|
testExtensionWithTelemetryPreferenceItems,
|
||||||
@ -106,18 +106,19 @@ describe("preferences - navigation to telemetry preferences", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("given extensions but no telemetry preference items, does not show link for telemetry preferences", () => {
|
it("given extensions but no telemetry preference items, does not show link for telemetry preferences", () => {
|
||||||
const testExtensionWithTelemetryPreferenceItems =
|
const getRendererExtensionFake = getRendererExtensionFakeFor(applicationBuilder);
|
||||||
getRendererExtensionFake({
|
const testExtensionWithTelemetryPreferenceItems = getRendererExtensionFake({
|
||||||
id: "some-test-extension-id",
|
id: "some-test-extension-id",
|
||||||
appPreferences: [
|
name: "some-test-extension-name",
|
||||||
{
|
appPreferences: [
|
||||||
title: "irrelevant",
|
{
|
||||||
id: "irrelevant",
|
title: "irrelevant",
|
||||||
showInPreferencesTab: "not-telemetry",
|
id: "irrelevant",
|
||||||
components: { Hint: () => <div />, Input: () => <div /> },
|
showInPreferencesTab: "not-telemetry",
|
||||||
},
|
components: { Hint: () => <div />, Input: () => <div /> },
|
||||||
],
|
},
|
||||||
});
|
],
|
||||||
|
});
|
||||||
|
|
||||||
applicationBuilder.addExtensions(
|
applicationBuilder.addExtensions(
|
||||||
testExtensionWithTelemetryPreferenceItems,
|
testExtensionWithTelemetryPreferenceItems,
|
||||||
@ -186,8 +187,9 @@ describe("preferences - navigation to telemetry preferences", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const extensionStubWithTelemetryPreferenceItems = {
|
const extensionStubWithTelemetryPreferenceItems: FakeExtensionData = {
|
||||||
id: "some-test-extension-id",
|
id: "some-test-extension-id",
|
||||||
|
name: "some-test-extension-name",
|
||||||
appPreferences: [
|
appPreferences: [
|
||||||
{
|
{
|
||||||
title: "Some telemetry-preference item",
|
title: "Some telemetry-preference item",
|
||||||
|
|||||||
@ -7,10 +7,9 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge
|
|||||||
import { getApplicationBuilder } 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 userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
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 { observable } from "mobx";
|
||||||
import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable";
|
import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable";
|
||||||
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
|
|
||||||
describe("preferences - navigation to terminal preferences", () => {
|
describe("preferences - navigation to terminal preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -26,12 +25,11 @@ describe("preferences - navigation to terminal preferences", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
|
||||||
rendererDi.override(defaultShellInjectable, () => "some-default-shell");
|
rendererDi.override(defaultShellInjectable, () => "some-default-shell");
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = ({ themeOptions: [] }) as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,7 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get-
|
|||||||
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
||||||
import type { UserStore } from "../../common/user-store";
|
import type { UserStore } from "../../common/user-store";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
||||||
import type { ThemeStore } from "../../renderer/theme.store";
|
import ipcRendererInjectable from "../../renderer/app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
|
||||||
import themeStoreInjectable from "../../renderer/theme-store.injectable";
|
|
||||||
|
|
||||||
describe("preferences - navigation using application menu", () => {
|
describe("preferences - navigation using application menu", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
@ -27,10 +26,10 @@ describe("preferences - navigation using application menu", () => {
|
|||||||
} as unknown as UserStore;
|
} as unknown as UserStore;
|
||||||
|
|
||||||
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
rendererDi.override(userStoreInjectable, () => userStoreStub);
|
||||||
|
rendererDi.override(ipcRendererInjectable, () => ({
|
||||||
const themeStoreStub = { themeOptions: [] } as unknown as ThemeStore;
|
on: jest.fn(),
|
||||||
|
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
|
||||||
rendererDi.override(themeStoreInjectable, () => themeStoreStub);
|
} as never));
|
||||||
});
|
});
|
||||||
|
|
||||||
rendered = await applicationBuilder.render();
|
rendered = await applicationBuilder.render();
|
||||||
|
|||||||
12
src/behaviours/utils.ts
Normal file
12
src/behaviours/utils.ts
Normal 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);
|
||||||
|
}
|
||||||
@ -27,16 +27,17 @@ exports[`welcome - navigation using application menu when navigating to welcome
|
|||||||
style="width: 320px;"
|
style="width: 320px;"
|
||||||
>
|
>
|
||||||
<h2>
|
<h2>
|
||||||
Welcome to
|
Welcome to OpenLens 5!
|
||||||
OpenLens
|
|
||||||
5!
|
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<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.
|
To get you started we have auto-detected your clusters in your
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
If you have any questions or feedback, please join our
|
|
||||||
|
|
||||||
|
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
|
<a
|
||||||
class="link"
|
class="link"
|
||||||
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
|
href="https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ"
|
||||||
|
|||||||
@ -30,9 +30,9 @@ interface TestStoreModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TestStore extends BaseStore<TestStoreModel> {
|
class TestStore extends BaseStore<TestStoreModel> {
|
||||||
@observable a: string;
|
@observable a = "";
|
||||||
@observable b: string;
|
@observable b = "";
|
||||||
@observable c: string;
|
@observable c = "";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
@ -90,7 +90,6 @@ describe("BaseStore", () => {
|
|||||||
|
|
||||||
await mainDi.runSetups();
|
await mainDi.runSetups();
|
||||||
|
|
||||||
store = undefined;
|
|
||||||
TestStore.resetInstance();
|
TestStore.resetInstance();
|
||||||
|
|
||||||
const mockOpts = {
|
const mockOpts = {
|
||||||
|
|||||||
@ -14,16 +14,13 @@ import { stdout, stderr } from "process";
|
|||||||
import getCustomKubeConfigDirectoryInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable";
|
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 clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
|
||||||
import type { ClusterModel } from "../cluster-types";
|
import type { ClusterModel } from "../cluster-types";
|
||||||
import type {
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
DiContainer,
|
|
||||||
} from "@ogre-tools/injectable";
|
|
||||||
|
|
||||||
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
|
import { createClusterInjectionToken } from "../cluster/create-cluster-injection-token";
|
||||||
|
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.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";
|
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
console = new Console(stdout, stderr);
|
console = new Console(stdout, stderr);
|
||||||
|
|
||||||
@ -148,6 +145,8 @@ describe("cluster-store", () => {
|
|||||||
it("adds new cluster to store", async () => {
|
it("adds new cluster to store", async () => {
|
||||||
const storedCluster = clusterStore.getById("foo");
|
const storedCluster = clusterStore.getById("foo");
|
||||||
|
|
||||||
|
assert(storedCluster);
|
||||||
|
|
||||||
expect(storedCluster.id).toBe("foo");
|
expect(storedCluster.id).toBe("foo");
|
||||||
expect(storedCluster.preferences.terminalCWD).toBe("/some-directory-for-user-data");
|
expect(storedCluster.preferences.terminalCWD).toBe("/some-directory-for-user-data");
|
||||||
expect(storedCluster.preferences.icon).toBe(
|
expect(storedCluster.preferences.icon).toBe(
|
||||||
@ -249,6 +248,8 @@ describe("cluster-store", () => {
|
|||||||
it("allows to retrieve a cluster", () => {
|
it("allows to retrieve a cluster", () => {
|
||||||
const storedCluster = clusterStore.getById("cluster1");
|
const storedCluster = clusterStore.getById("cluster1");
|
||||||
|
|
||||||
|
assert(storedCluster);
|
||||||
|
|
||||||
expect(storedCluster.id).toBe("cluster1");
|
expect(storedCluster.id).toBe("cluster1");
|
||||||
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
|
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
|
||||||
});
|
});
|
||||||
@ -379,6 +380,7 @@ users:
|
|||||||
it("migrates to modern format with icon not in file", async () => {
|
it("migrates to modern format with icon not in file", async () => {
|
||||||
const { icon } = clusterStore.clustersList[0].preferences;
|
const { icon } = clusterStore.clustersList[0].preferences;
|
||||||
|
|
||||||
|
assert(icon);
|
||||||
expect(icon.startsWith("data:;base64,")).toBe(true);
|
expect(icon.startsWith("data:;base64,")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import type { AppEvent } from "../app-event-bus/event-bus";
|
import type { AppEvent } from "../app-event-bus/event-bus";
|
||||||
import { appEventBus } 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";
|
import { stdout, stderr } from "process";
|
||||||
|
|
||||||
console = new Console(stdout, stderr);
|
console = new Console(stdout, stderr);
|
||||||
@ -13,14 +13,15 @@ console = new Console(stdout, stderr);
|
|||||||
describe("event bus tests", () => {
|
describe("event bus tests", () => {
|
||||||
describe("emit", () => {
|
describe("emit", () => {
|
||||||
it("emits an event", () => {
|
it("emits an event", () => {
|
||||||
let event: AppEvent = null;
|
let event: AppEvent | undefined;
|
||||||
|
|
||||||
appEventBus.addListener((data) => {
|
appEventBus.addListener((data) => {
|
||||||
event = data;
|
event = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
appEventBus.emit({ name: "foo", action: "bar" });
|
appEventBus.emit({ name: "foo", action: "bar" });
|
||||||
expect(event.name).toBe("foo");
|
assert(event);
|
||||||
|
expect(event?.name).toBe("foo");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,7 +21,7 @@ describe("EventEmitter", () => {
|
|||||||
let called = false;
|
let called = false;
|
||||||
const e = new EventEmitter<[]>();
|
const e = new EventEmitter<[]>();
|
||||||
|
|
||||||
e.addListener(() => 0 as any, {});
|
e.addListener(() => 0 as never, {});
|
||||||
e.addListener(() => { called = true; }, {});
|
e.addListener(() => { called = true; }, {});
|
||||||
e.emit();
|
e.emit();
|
||||||
|
|
||||||
|
|||||||
@ -5,69 +5,21 @@
|
|||||||
|
|
||||||
import { anyObject } from "jest-mock-extended";
|
import { anyObject } from "jest-mock-extended";
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
import logger from "../../main/logger";
|
|
||||||
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
|
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.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";
|
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
|
||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import hotbarStoreInjectable from "../hotbar-store.injectable";
|
import hotbarStoreInjectable from "../hotbars/store.injectable";
|
||||||
import { HotbarStore } from "../hotbar-store";
|
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";
|
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
||||||
|
import loggerInjectable from "../logger.injectable";
|
||||||
|
import type { Logger } from "../logger";
|
||||||
|
|
||||||
jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
console.log("I am here as reminder against mockfs (and to fix console logging)");
|
||||||
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: {},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
|
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
|
||||||
return {
|
return {
|
||||||
@ -84,69 +36,91 @@ function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKi
|
|||||||
} as CatalogEntity;
|
} 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", () => {
|
describe("HotbarStore", () => {
|
||||||
let di: DiContainer;
|
let di: DiContainer;
|
||||||
let hotbarStore: HotbarStore;
|
let hotbarStore: HotbarStore;
|
||||||
|
let testCluster: CatalogEntity;
|
||||||
|
let minikubeCluster: CatalogEntity;
|
||||||
|
let awsCluster: CatalogEntity;
|
||||||
|
let loggerMock: jest.Mocked<Logger>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
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);
|
||||||
|
|
||||||
|
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||||
|
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
|
||||||
|
|
||||||
|
catalogEntityRegistry.addComputedSource("some-id", computed(() => [
|
||||||
|
testCluster,
|
||||||
|
minikubeCluster,
|
||||||
|
awsCluster,
|
||||||
|
catalogCatalogEntity,
|
||||||
|
]));
|
||||||
|
|
||||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||||
di.permitSideEffects(appVersionInjectable);
|
di.permitSideEffects(appVersionInjectable);
|
||||||
|
di.permitSideEffects(hotbarStoreInjectable);
|
||||||
di.override(hotbarStoreInjectable, () => {
|
|
||||||
HotbarStore.resetInstance();
|
|
||||||
|
|
||||||
return HotbarStore.createInstance({
|
|
||||||
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("given no migrations", () => {
|
describe("given no previous data in store, running all migrations", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mockFs();
|
mockFs();
|
||||||
|
|
||||||
@ -174,7 +148,7 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("initially adds catalog entity as first item", () => {
|
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", () => {
|
it("adds items", () => {
|
||||||
@ -186,7 +160,7 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
it("removes items", () => {
|
it("removes items", () => {
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.removeFromHotbar("test");
|
hotbarStore.removeFromHotbar("some-test-id");
|
||||||
hotbarStore.removeFromHotbar("catalog-entity");
|
hotbarStore.removeFromHotbar("catalog-entity");
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||||
|
|
||||||
@ -211,7 +185,7 @@ describe("HotbarStore", () => {
|
|||||||
hotbarStore.restackItems(1, 5);
|
hotbarStore.restackItems(1, 5);
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[5]).toBeTruthy();
|
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", () => {
|
it("moves items down", () => {
|
||||||
@ -224,7 +198,7 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
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", () => {
|
it("moves items up", () => {
|
||||||
@ -237,28 +211,21 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
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", () => {
|
it("logs an error if cellIndex is out of bounds", () => {
|
||||||
hotbarStore.add({ name: "hottest", id: "hottest" });
|
hotbarStore.add({ name: "hottest", id: "hottest" });
|
||||||
hotbarStore.setActiveHotbar("hottest");
|
hotbarStore.setActiveHotbar("hottest");
|
||||||
|
|
||||||
const { error } = logger;
|
|
||||||
const mocked = jest.fn();
|
|
||||||
|
|
||||||
logger.error = mocked;
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, -1);
|
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);
|
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);
|
hotbarStore.addToHotbar(testCluster, 13);
|
||||||
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());
|
||||||
|
|
||||||
logger.error = error;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws an error if getId is invalid or returns not a string", () => {
|
it("throws an error if getId is invalid or returns not a string", () => {
|
||||||
@ -275,7 +242,7 @@ describe("HotbarStore", () => {
|
|||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.restackItems(1, 1);
|
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", () => {
|
it("new items takes first empty cell", () => {
|
||||||
@ -284,7 +251,7 @@ describe("HotbarStore", () => {
|
|||||||
hotbarStore.restackItems(0, 3);
|
hotbarStore.restackItems(0, 3);
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
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", () => {
|
it("throws if invalid arguments provided", () => {
|
||||||
@ -315,7 +282,7 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("given pre beta-5 configurations", () => {
|
describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const configurationToBeMigrated = {
|
const configurationToBeMigrated = {
|
||||||
"some-electron-app-path-for-user-data": {
|
"some-electron-app-path-for-user-data": {
|
||||||
@ -332,7 +299,7 @@ describe("HotbarStore", () => {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
entity: {
|
entity: {
|
||||||
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
|
uid: "some-aws-id",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -381,15 +348,17 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
mockFs(configurationToBeMigrated);
|
mockFs(configurationToBeMigrated);
|
||||||
|
|
||||||
|
di.override(appVersionInjectable, () => "5.0.0-beta.10");
|
||||||
|
|
||||||
await di.runSetups();
|
await di.runSetups();
|
||||||
|
|
||||||
hotbarStore = di.inject(hotbarStoreInjectable);
|
hotbarStore = di.inject(hotbarStoreInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows to retrieve a hotbar", () => {
|
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", () => {
|
it("clears cells without entity", () => {
|
||||||
@ -403,17 +372,9 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
expect(items[0]).toEqual({
|
expect(items[0]).toEqual({
|
||||||
entity: {
|
entity: {
|
||||||
name: "mycluster",
|
name: "my-aws-cluster",
|
||||||
source: "local",
|
source: "local",
|
||||||
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
|
uid: "some-aws-id",
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(items[1]).toEqual({
|
|
||||||
entity: {
|
|
||||||
name: "my_shiny_cluster",
|
|
||||||
source: "remote",
|
|
||||||
uid: "55b42c3c7ba3b04193416cda405269a5",
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -6,13 +6,14 @@ import https from "https";
|
|||||||
import os from "os";
|
import os from "os";
|
||||||
import { getMacRootCA, getWinRootCA, injectCAs, DSTRootCAX3 } from "../system-ca";
|
import { getMacRootCA, getWinRootCA, injectCAs, DSTRootCAX3 } from "../system-ca";
|
||||||
import { dependencies, devDependencies } from "../../../package.json";
|
import { dependencies, devDependencies } from "../../../package.json";
|
||||||
|
import assert from "assert";
|
||||||
|
|
||||||
const deps = { ...dependencies, ...devDependencies };
|
const deps = { ...dependencies, ...devDependencies };
|
||||||
|
|
||||||
// Skip the test if mac-ca is not installed, or os is not darwin
|
// 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", () => {
|
(deps["mac-ca"] && os.platform().includes("darwin") ? describe: describe.skip)("inject CA for Mac", () => {
|
||||||
// for reset https.globalAgent.options.ca after testing
|
// for reset https.globalAgent.options.ca after testing
|
||||||
let _ca: string | Buffer | (string | Buffer)[];
|
let _ca: string | Buffer | (string | Buffer)[] | undefined;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
_ca = https.globalAgent.options.ca;
|
_ca = https.globalAgent.options.ca;
|
||||||
@ -44,6 +45,7 @@ const deps = { ...dependencies, ...devDependencies };
|
|||||||
injectCAs(osxCAs);
|
injectCAs(osxCAs);
|
||||||
const injected = https.globalAgent.options.ca;
|
const injected = https.globalAgent.options.ca;
|
||||||
|
|
||||||
|
assert(injected);
|
||||||
expect(injected.includes(DSTRootCAX3)).toBeFalsy();
|
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
|
// 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", () => {
|
(deps["win-ca"] && os.platform().includes("win32") ? describe: describe.skip)("inject CA for Windows", () => {
|
||||||
// for reset https.globalAgent.options.ca after testing
|
// for reset https.globalAgent.options.ca after testing
|
||||||
let _ca: string | Buffer | (string | Buffer)[];
|
let _ca: string | Buffer | (string | Buffer)[] | undefined;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
_ca = https.globalAgent.options.ca;
|
_ca = https.globalAgent.options.ca;
|
||||||
|
|||||||
@ -30,7 +30,7 @@ import userStoreInjectable from "../user-store/user-store.injectable";
|
|||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
|
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
|
||||||
import { defaultTheme } from "../vars";
|
import { defaultThemeId } from "../vars";
|
||||||
import writeFileInjectable from "../fs/write-file.injectable";
|
import writeFileInjectable from "../fs/write-file.injectable";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
import getConfigurationFileModelInjectable
|
import getConfigurationFileModelInjectable
|
||||||
@ -49,7 +49,7 @@ describe("user store tests", () => {
|
|||||||
|
|
||||||
mockFs();
|
mockFs();
|
||||||
|
|
||||||
di.override(writeFileInjectable, () => () => undefined);
|
di.override(writeFileInjectable, () => () => Promise.resolve());
|
||||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||||
di.override(userStoreInjectable, () => UserStore.createInstance());
|
di.override(userStoreInjectable, () => UserStore.createInstance());
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ describe("user store tests", () => {
|
|||||||
userStore.httpsProxy = "abcd://defg";
|
userStore.httpsProxy = "abcd://defg";
|
||||||
|
|
||||||
expect(userStore.httpsProxy).toBe("abcd://defg");
|
expect(userStore.httpsProxy).toBe("abcd://defg");
|
||||||
expect(userStore.colorTheme).toBe(defaultTheme);
|
expect(userStore.colorTheme).toBe(defaultThemeId);
|
||||||
|
|
||||||
userStore.colorTheme = "light";
|
userStore.colorTheme = "light";
|
||||||
expect(userStore.colorTheme).toBe("light");
|
expect(userStore.colorTheme).toBe("light");
|
||||||
@ -89,7 +89,7 @@ describe("user store tests", () => {
|
|||||||
it("correctly resets theme to default value", async () => {
|
it("correctly resets theme to default value", async () => {
|
||||||
userStore.colorTheme = "some other theme";
|
userStore.colorTheme = "some other theme";
|
||||||
userStore.resetTheme();
|
userStore.resetTheme();
|
||||||
expect(userStore.colorTheme).toBe(defaultTheme);
|
expect(userStore.colorTheme).toBe(defaultThemeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("correctly calculates if the last seen version is an old release", () => {
|
it("correctly calculates if the last seen version is an old release", () => {
|
||||||
|
|||||||
@ -2,20 +2,30 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { kubernetesClusterCategory } from "../kubernetes-cluster";
|
|
||||||
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
|
import kubernetesClusterCategoryInjectable from "../../catalog/categories/kubernetes-cluster.injectable";
|
||||||
|
import type { KubernetesClusterCategory } from "../kubernetes-cluster";
|
||||||
|
|
||||||
|
|
||||||
describe("kubernetesClusterCategory", () => {
|
describe("kubernetesClusterCategory", () => {
|
||||||
|
let kubernetesClusterCategory: KubernetesClusterCategory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting();
|
||||||
|
|
||||||
|
kubernetesClusterCategory = di.inject(kubernetesClusterCategoryInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
describe("filteredItems", () => {
|
describe("filteredItems", () => {
|
||||||
const item1 = {
|
const item1 = {
|
||||||
icon: "Icon",
|
icon: "Icon",
|
||||||
title: "Title",
|
title: "Title",
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
};
|
};
|
||||||
const item2 = {
|
const item2 = {
|
||||||
icon: "Icon 2",
|
icon: "Icon 2",
|
||||||
title: "Title 2",
|
title: "Title 2",
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -5,8 +5,7 @@
|
|||||||
|
|
||||||
import { navigate } from "../../renderer/navigation";
|
import { navigate } from "../../renderer/navigation";
|
||||||
import type { CatalogEntityMetadata, CatalogEntitySpec, CatalogEntityStatus } from "../catalog";
|
import type { CatalogEntityMetadata, CatalogEntitySpec, CatalogEntityStatus } from "../catalog";
|
||||||
import { CatalogCategory, CatalogEntity } from "../catalog";
|
import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity";
|
||||||
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry";
|
|
||||||
|
|
||||||
interface GeneralEntitySpec extends CatalogEntitySpec {
|
interface GeneralEntitySpec extends CatalogEntitySpec {
|
||||||
path: string;
|
path: string;
|
||||||
@ -23,18 +22,6 @@ export class GeneralEntity extends CatalogEntity<CatalogEntityMetadata, CatalogE
|
|||||||
async onRun() {
|
async onRun() {
|
||||||
navigate(this.spec.path);
|
navigate(this.spec.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSettingsOpen(): void {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onDetailsOpen(): void {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onContextMenuOpen(): void {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GeneralCategory extends CatalogCategory {
|
export class GeneralCategory extends CatalogCategory {
|
||||||
@ -47,15 +34,10 @@ export class GeneralCategory extends CatalogCategory {
|
|||||||
public spec = {
|
public spec = {
|
||||||
group: "entity.k8slens.dev",
|
group: "entity.k8slens.dev",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
categoryVersion("v1alpha1", GeneralEntity),
|
||||||
name: "v1alpha1",
|
|
||||||
entityClass: GeneralEntity,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
names: {
|
names: {
|
||||||
kind: "General",
|
kind: "General",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogCategoryRegistry.add(new GeneralCategory());
|
|
||||||
|
|||||||
@ -3,13 +3,12 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry";
|
|
||||||
import type { CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus, CatalogCategorySpec } from "../catalog";
|
import type { CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus, CatalogCategorySpec } from "../catalog";
|
||||||
import { CatalogEntity, CatalogCategory } from "../catalog";
|
import { CatalogEntity, CatalogCategory, categoryVersion } from "../catalog/catalog-entity";
|
||||||
import { ClusterStore } from "../cluster-store/cluster-store";
|
import { ClusterStore } from "../cluster-store/cluster-store";
|
||||||
import { broadcastMessage } from "../ipc";
|
import { broadcastMessage } from "../ipc";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import type { CatalogEntitySpec } from "../catalog/catalog-entity";
|
import type { CatalogEntityConstructor, CatalogEntitySpec } from "../catalog/catalog-entity";
|
||||||
import { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
import { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
||||||
import { requestClusterActivation, requestClusterDisconnection } from "../../renderer/ipc";
|
import { requestClusterActivation, requestClusterDisconnection } from "../../renderer/ipc";
|
||||||
import KubeClusterCategoryIcon from "./icons/kubernetes.svg";
|
import KubeClusterCategoryIcon from "./icons/kubernetes.svg";
|
||||||
@ -60,6 +59,10 @@ export type KubernetesClusterStatusPhase = "connected" | "connecting" | "disconn
|
|||||||
export interface KubernetesClusterStatus extends CatalogEntityStatus {
|
export interface KubernetesClusterStatus extends CatalogEntityStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isKubernetesCluster(item: unknown): item is KubernetesCluster {
|
||||||
|
return item instanceof KubernetesCluster;
|
||||||
|
}
|
||||||
|
|
||||||
export class KubernetesCluster<
|
export class KubernetesCluster<
|
||||||
Metadata extends KubernetesClusterMetadata = KubernetesClusterMetadata,
|
Metadata extends KubernetesClusterMetadata = KubernetesClusterMetadata,
|
||||||
Status extends KubernetesClusterStatus = KubernetesClusterStatus,
|
Status extends KubernetesClusterStatus = KubernetesClusterStatus,
|
||||||
@ -99,7 +102,7 @@ export class KubernetesCluster<
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
async onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
||||||
if (!this.metadata.source || this.metadata.source === "local") {
|
if (!this.metadata.source || this.metadata.source === "local") {
|
||||||
context.menuItems.push({
|
context.menuItems.push({
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
@ -128,14 +131,10 @@ export class KubernetesCluster<
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogCategoryRegistry
|
|
||||||
.getCategoryForEntity<KubernetesClusterCategory>(this)
|
|
||||||
?.emit("contextMenuOpen", this, context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class KubernetesClusterCategory extends CatalogCategory {
|
export class KubernetesClusterCategory extends CatalogCategory {
|
||||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||||
public readonly kind = "CatalogCategory";
|
public readonly kind = "CatalogCategory";
|
||||||
public metadata = {
|
public metadata = {
|
||||||
@ -145,17 +144,10 @@ class KubernetesClusterCategory extends CatalogCategory {
|
|||||||
public spec: CatalogCategorySpec = {
|
public spec: CatalogCategorySpec = {
|
||||||
group: "entity.k8slens.dev",
|
group: "entity.k8slens.dev",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
categoryVersion("v1alpha1", KubernetesCluster as CatalogEntityConstructor<KubernetesCluster>),
|
||||||
name: "v1alpha1",
|
|
||||||
entityClass: KubernetesCluster,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
names: {
|
names: {
|
||||||
kind: "KubernetesCluster",
|
kind: "KubernetesCluster",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const kubernetesClusterCategory = new KubernetesClusterCategory();
|
|
||||||
|
|
||||||
catalogCategoryRegistry.add(kubernetesClusterCategory);
|
|
||||||
|
|||||||
@ -4,8 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
||||||
import { CatalogCategory, CatalogEntity } from "../catalog";
|
import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity";
|
||||||
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry";
|
|
||||||
import { productName } from "../vars";
|
import { productName } from "../vars";
|
||||||
import { WeblinkStore } from "../weblink-store";
|
import { WeblinkStore } from "../weblink-store";
|
||||||
|
|
||||||
@ -30,11 +29,7 @@ export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus,
|
|||||||
window.open(this.spec.url, "_blank");
|
window.open(this.spec.url, "_blank");
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSettingsOpen(): void {
|
onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onContextMenuOpen(context: CatalogEntityContextMenuContext) {
|
|
||||||
if (this.metadata.source === "local") {
|
if (this.metadata.source === "local") {
|
||||||
context.menuItems.push({
|
context.menuItems.push({
|
||||||
title: "Delete",
|
title: "Delete",
|
||||||
@ -45,10 +40,6 @@ export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus,
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogCategoryRegistry
|
|
||||||
.getCategoryForEntity<WebLinkCategory>(this)
|
|
||||||
?.emit("contextMenuOpen", this, context);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,15 +53,10 @@ export class WebLinkCategory extends CatalogCategory {
|
|||||||
public spec = {
|
public spec = {
|
||||||
group: "entity.k8slens.dev",
|
group: "entity.k8slens.dev",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
categoryVersion("v1alpha1", WebLink),
|
||||||
name: "v1alpha1",
|
|
||||||
entityClass: WebLink,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
names: {
|
names: {
|
||||||
kind: "WebLink",
|
kind: "WebLink",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogCategoryRegistry.add(new WebLinkCategory());
|
|
||||||
|
|||||||
@ -3,96 +3,10 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { action, computed, observable, makeObservable } from "mobx";
|
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
import { once } from "lodash";
|
import catalogCategoryRegistryInjectable from "./category-registry.injectable";
|
||||||
import { iter, getOrInsertMap, strictSet } from "../utils";
|
|
||||||
import type { Disposer } from "../utils";
|
|
||||||
import type { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
|
||||||
|
|
||||||
export type CategoryFilter = (category: CatalogCategory) => any;
|
/**
|
||||||
|
* @deprecated use `di.inject(catalogCategoryRegistryInjectable)` instead
|
||||||
export class CatalogCategoryRegistry {
|
*/
|
||||||
protected categories = observable.set<CatalogCategory>();
|
export const catalogCategoryRegistry = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable);
|
||||||
protected groupKinds = new Map<string, Map<string, CatalogCategory>>();
|
|
||||||
protected filters = observable.set<CategoryFilter>([], {
|
|
||||||
deep: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action add(category: CatalogCategory): Disposer {
|
|
||||||
const byGroup = getOrInsertMap(this.groupKinds, category.spec.group);
|
|
||||||
|
|
||||||
this.categories.add(category);
|
|
||||||
strictSet(byGroup, category.spec.names.kind, category);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
this.categories.delete(category);
|
|
||||||
byGroup.delete(category.spec.names.kind);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get items() {
|
|
||||||
return Array.from(this.categories);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get filteredItems() {
|
|
||||||
return Array.from(
|
|
||||||
iter.reduce(
|
|
||||||
this.filters,
|
|
||||||
iter.filter,
|
|
||||||
this.items.values(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getForGroupKind<T extends CatalogCategory>(group: string, kind: string): T | undefined {
|
|
||||||
return this.groupKinds.get(group)?.get(kind) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
getEntityForData(data: CatalogEntityData & CatalogEntityKindData) {
|
|
||||||
const category = this.getCategoryForEntity(data);
|
|
||||||
|
|
||||||
if (!category) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const splitApiVersion = data.apiVersion.split("/");
|
|
||||||
const version = splitApiVersion[1];
|
|
||||||
|
|
||||||
const specVersion = category.spec.versions.find((v) => v.name === version);
|
|
||||||
|
|
||||||
if (!specVersion) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new specVersion.entityClass(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCategoryForEntity<T extends CatalogCategory>(data: CatalogEntityData & CatalogEntityKindData): T | undefined {
|
|
||||||
const splitApiVersion = data.apiVersion.split("/");
|
|
||||||
const group = splitApiVersion[0];
|
|
||||||
|
|
||||||
return this.getForGroupKind(group, data.kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
getByName(name: string) {
|
|
||||||
return this.items.find(category => category.metadata?.name == name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new filter to the set of category filters
|
|
||||||
* @param fn The function that should return a truthy value if that category should be displayed
|
|
||||||
* @returns A function to remove that filter
|
|
||||||
*/
|
|
||||||
addCatalogCategoryFilter(fn: CategoryFilter): Disposer {
|
|
||||||
this.filters.add(fn);
|
|
||||||
|
|
||||||
return once(() => void this.filters.delete(fn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const catalogCategoryRegistry = new CatalogCategoryRegistry();
|
|
||||||
|
|||||||
@ -11,19 +11,19 @@ import type { Disposer } from "../utils";
|
|||||||
import { iter } from "../utils";
|
import { iter } from "../utils";
|
||||||
import type { CategoryColumnRegistration } from "../../renderer/components/+catalog/custom-category-columns";
|
import type { CategoryColumnRegistration } from "../../renderer/components/+catalog/custom-category-columns";
|
||||||
|
|
||||||
type ExtractEntityMetadataType<Entity> = Entity extends CatalogEntity<infer Metadata> ? Metadata : never;
|
export type CatalogEntityDataFor<Entity> = Entity extends CatalogEntity<infer Metadata, infer Status, infer Spec>
|
||||||
type ExtractEntityStatusType<Entity> = Entity extends CatalogEntity<any, infer Status> ? Status : never;
|
? CatalogEntityData<Metadata, Status, Spec>
|
||||||
type ExtractEntitySpecType<Entity> = Entity extends CatalogEntity<any, any, infer Spec> ? Spec : never;
|
: never;
|
||||||
|
|
||||||
|
export type CatalogEntityInstanceFrom<Constructor> = Constructor extends CatalogEntityConstructor<infer Entity>
|
||||||
|
? Entity
|
||||||
|
: never;
|
||||||
|
|
||||||
export type CatalogEntityConstructor<Entity extends CatalogEntity> = (
|
export type CatalogEntityConstructor<Entity extends CatalogEntity> = (
|
||||||
(new (data: CatalogEntityData<
|
new (data: CatalogEntityDataFor<Entity>) => Entity
|
||||||
ExtractEntityMetadataType<Entity>,
|
|
||||||
ExtractEntityStatusType<Entity>,
|
|
||||||
ExtractEntitySpecType<Entity>
|
|
||||||
>) => Entity)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
export interface CatalogCategoryVersion<Entity extends CatalogEntity> {
|
export interface CatalogCategoryVersion {
|
||||||
/**
|
/**
|
||||||
* The specific version that the associated constructor is for. This MUST be
|
* The specific version that the associated constructor is for. This MUST be
|
||||||
* a DNS label and SHOULD be of the form `vN`, `vNalphaY`, or `vNbetaY` where
|
* a DNS label and SHOULD be of the form `vN`, `vNalphaY`, or `vNbetaY` where
|
||||||
@ -35,19 +35,19 @@ export interface CatalogCategoryVersion<Entity extends CatalogEntity> {
|
|||||||
* - `v1alpha2`
|
* - `v1alpha2`
|
||||||
* - `v3beta2`
|
* - `v3beta2`
|
||||||
*/
|
*/
|
||||||
name: string;
|
readonly name: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constructor for the entities.
|
* The constructor for the entities.
|
||||||
*/
|
*/
|
||||||
entityClass: CatalogEntityConstructor<Entity>;
|
readonly entityClass: CatalogEntityConstructor<CatalogEntity>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CatalogCategorySpec {
|
export interface CatalogCategorySpec {
|
||||||
/**
|
/**
|
||||||
* The grouping for for the category. This MUST be a DNS label.
|
* The grouping for for the category. This MUST be a DNS label.
|
||||||
*/
|
*/
|
||||||
group: string;
|
readonly group: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The specific versions of the constructors.
|
* The specific versions of the constructors.
|
||||||
@ -56,18 +56,18 @@ export interface CatalogCategorySpec {
|
|||||||
* For example, if `group = "entity.k8slens.dev"` and there is an entry in `.versions` with
|
* For example, if `group = "entity.k8slens.dev"` and there is an entry in `.versions` with
|
||||||
* `name = "v1alpha1"` then the resulting `.apiVersion` MUST be `entity.k8slens.dev/v1alpha1`
|
* `name = "v1alpha1"` then the resulting `.apiVersion` MUST be `entity.k8slens.dev/v1alpha1`
|
||||||
*/
|
*/
|
||||||
versions: CatalogCategoryVersion<CatalogEntity>[];
|
readonly versions: CatalogCategoryVersion[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the concerning the category
|
* This is the concerning the category
|
||||||
*/
|
*/
|
||||||
names: {
|
readonly names: {
|
||||||
/**
|
/**
|
||||||
* The kind of entity that this category is for. This value MUST be a DNS
|
* The kind of entity that this category is for. This value MUST be a DNS
|
||||||
* label and MUST be equal to the `kind` fields that are produced by the
|
* label and MUST be equal to the `kind` fields that are produced by the
|
||||||
* `.versions.[] | .entityClass` fields.
|
* `.versions.[] | .entityClass` fields.
|
||||||
*/
|
*/
|
||||||
kind: string;
|
readonly kind: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +81,7 @@ export interface CatalogCategorySpec {
|
|||||||
*
|
*
|
||||||
* These columns will not be used in the "Browse" view.
|
* These columns will not be used in the "Browse" view.
|
||||||
*/
|
*/
|
||||||
displayColumns?: CategoryColumnRegistration[];
|
readonly displayColumns?: CategoryColumnRegistration[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,6 +109,30 @@ export interface CatalogCategoryEvents {
|
|||||||
contextMenuOpen: (entity: CatalogEntity, context: CatalogEntityContextMenuContext) => void;
|
contextMenuOpen: (entity: CatalogEntity, context: CatalogEntityContextMenuContext) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CatalogCategoryMetadata {
|
||||||
|
/**
|
||||||
|
* The name of your category. The category can be searched for by this
|
||||||
|
* value. This will also be used for the catalog menu.
|
||||||
|
*/
|
||||||
|
readonly name: string;
|
||||||
|
/**
|
||||||
|
* Either an `<svg>` or the name of an icon from {@link IconProps}
|
||||||
|
*/
|
||||||
|
readonly icon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function categoryVersion<
|
||||||
|
T extends CatalogEntity<Metadata, Status, Spec>,
|
||||||
|
Metadata extends CatalogEntityMetadata,
|
||||||
|
Status extends CatalogEntityStatus,
|
||||||
|
Spec extends CatalogEntitySpec,
|
||||||
|
>(name: string, entityClass: new (data: CatalogEntityData<Metadata, Status, Spec>) => T): CatalogCategoryVersion {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
entityClass: entityClass as CatalogEntityConstructor<T>,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class CatalogCategory extends (EventEmitter as new () => TypedEmitter<CatalogCategoryEvents>) {
|
export abstract class CatalogCategory extends (EventEmitter as new () => TypedEmitter<CatalogCategoryEvents>) {
|
||||||
/**
|
/**
|
||||||
* The version of category that you are wanting to declare.
|
* The version of category that you are wanting to declare.
|
||||||
@ -131,28 +155,17 @@ export abstract class CatalogCategory extends (EventEmitter as new () => TypedEm
|
|||||||
/**
|
/**
|
||||||
* The data about the category itself
|
* The data about the category itself
|
||||||
*/
|
*/
|
||||||
abstract readonly metadata: {
|
abstract readonly metadata: CatalogCategoryMetadata;
|
||||||
/**
|
|
||||||
* The name of your category. The category can be searched for by this
|
|
||||||
* value. This will also be used for the catalog menu.
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Either an `<svg>` or the name of an icon from {@link IconProps}
|
|
||||||
*/
|
|
||||||
icon: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most important part of a category, as it is where entity versions are declared.
|
* The most important part of a category, as it is where entity versions are declared.
|
||||||
*/
|
*/
|
||||||
abstract spec: CatalogCategorySpec;
|
abstract readonly spec: CatalogCategorySpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected filters = observable.set<AddMenuFilter>([], {
|
protected readonly filters = observable.set<AddMenuFilter>([], {
|
||||||
deep: false,
|
deep: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -217,14 +230,16 @@ export abstract class CatalogCategory extends (EventEmitter as new () => TypedEm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CatalogEntityMetadata {
|
export type EntityMetadataObject = { [Key in string]?: EntityMetadataValue };
|
||||||
|
export type EntityMetadataValue = string | number | boolean | EntityMetadataObject | undefined;
|
||||||
|
|
||||||
|
export interface CatalogEntityMetadata extends EntityMetadataObject {
|
||||||
uid: string;
|
uid: string;
|
||||||
name: string;
|
name: string;
|
||||||
shortName?: string;
|
shortName?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
labels: Record<string, string>;
|
labels: Record<string, string>;
|
||||||
[key: string]: string | object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CatalogEntityStatus {
|
export interface CatalogEntityStatus {
|
||||||
@ -392,7 +407,7 @@ export abstract class CatalogEntity<
|
|||||||
return this.status.enabled ?? true;
|
return this.status.enabled ?? true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract onRun?(context: CatalogEntityActionContext): void | Promise<void>;
|
public onRun?(context: CatalogEntityActionContext): void | Promise<void>;
|
||||||
public abstract onContextMenuOpen(context: CatalogEntityContextMenuContext): void | Promise<void>;
|
public onContextMenuOpen?(context: CatalogEntityContextMenuContext): void | Promise<void>;
|
||||||
public abstract onSettingsOpen(context: CatalogEntitySettingsContext): void | Promise<void>;
|
public onSettingsOpen?(context: CatalogEntitySettingsContext): void | Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/common/catalog/categories/general.injectable.ts
Normal file
15
src/common/catalog/categories/general.injectable.ts
Normal 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 { GeneralCategory } from "../../catalog-entities";
|
||||||
|
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||||
|
|
||||||
|
const generalCategoryInjectable = getInjectable({
|
||||||
|
id: "general-category",
|
||||||
|
instantiate: () => new GeneralCategory(),
|
||||||
|
injectionToken: builtInCategoryInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default generalCategoryInjectable;
|
||||||
@ -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 { KubernetesClusterCategory } from "../../catalog-entities/kubernetes-cluster";
|
||||||
|
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||||
|
|
||||||
|
const kubernetesClusterCategoryInjectable = getInjectable({
|
||||||
|
id: "kubernetes-cluster-category",
|
||||||
|
instantiate: () => new KubernetesClusterCategory(),
|
||||||
|
injectionToken: builtInCategoryInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default kubernetesClusterCategoryInjectable;
|
||||||
15
src/common/catalog/categories/weblink.injectable.ts
Normal file
15
src/common/catalog/categories/weblink.injectable.ts
Normal 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 { WebLinkCategory } from "../../catalog-entities";
|
||||||
|
import { builtInCategoryInjectionToken } from "../category-registry.injectable";
|
||||||
|
|
||||||
|
const weblinkCategoryInjectable = getInjectable({
|
||||||
|
id: "weblink-category",
|
||||||
|
instantiate: () => new WebLinkCategory(),
|
||||||
|
injectionToken: builtInCategoryInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default weblinkCategoryInjectable;
|
||||||
27
src/common/catalog/category-registry.injectable.ts
Normal file
27
src/common/catalog/category-registry.injectable.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import type { CatalogCategory } from "./catalog-entity";
|
||||||
|
import { CatalogCategoryRegistry } from "./category-registry";
|
||||||
|
|
||||||
|
export const builtInCategoryInjectionToken = getInjectionToken<CatalogCategory>({
|
||||||
|
id: "built-in-category-token",
|
||||||
|
});
|
||||||
|
|
||||||
|
const catalogCategoryRegistryInjectable = getInjectable({
|
||||||
|
id: "catalog-category-registry",
|
||||||
|
instantiate: (di) => {
|
||||||
|
const registry = new CatalogCategoryRegistry();
|
||||||
|
const categories = di.injectMany(builtInCategoryInjectionToken);
|
||||||
|
|
||||||
|
for (const category of categories) {
|
||||||
|
registry.add(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
return registry;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default catalogCategoryRegistryInjectable;
|
||||||
103
src/common/catalog/category-registry.ts
Normal file
103
src/common/catalog/category-registry.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { action, computed, observable, makeObservable } from "mobx";
|
||||||
|
import { once } from "lodash";
|
||||||
|
import { iter, getOrInsertMap, strictSet } from "../utils";
|
||||||
|
import type { Disposer } from "../utils";
|
||||||
|
import type { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
||||||
|
|
||||||
|
export type CategoryFilter = (category: CatalogCategory) => any;
|
||||||
|
|
||||||
|
export class CatalogCategoryRegistry {
|
||||||
|
protected readonly categories = observable.set<CatalogCategory>();
|
||||||
|
protected readonly groupKinds = new Map<string, Map<string, CatalogCategory>>();
|
||||||
|
protected readonly filters = observable.set<CategoryFilter>([], {
|
||||||
|
deep: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
makeObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action add(category: CatalogCategory): Disposer {
|
||||||
|
const byGroup = getOrInsertMap(this.groupKinds, category.spec.group);
|
||||||
|
|
||||||
|
this.categories.add(category);
|
||||||
|
strictSet(byGroup, category.spec.names.kind, category);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
this.categories.delete(category);
|
||||||
|
byGroup.delete(category.spec.names.kind);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get items() {
|
||||||
|
return Array.from(this.categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get filteredItems() {
|
||||||
|
return Array.from(
|
||||||
|
iter.reduce(
|
||||||
|
this.filters,
|
||||||
|
iter.filter,
|
||||||
|
this.items.values(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getForGroupKind<T extends CatalogCategory>(group: string, kind: string): T | undefined {
|
||||||
|
return this.groupKinds.get(group)?.get(kind) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEntityForData(data: CatalogEntityData & CatalogEntityKindData) {
|
||||||
|
const category = this.getCategoryForEntity(data);
|
||||||
|
|
||||||
|
if (!category) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitApiVersion = data.apiVersion.split("/");
|
||||||
|
const version = splitApiVersion[1];
|
||||||
|
|
||||||
|
const specVersion = category.spec.versions.find((v) => v.name === version);
|
||||||
|
|
||||||
|
if (!specVersion) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new specVersion.entityClass(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCategoryForEntity({ kind, apiVersion }: CatalogEntityData & CatalogEntityKindData): boolean {
|
||||||
|
const splitApiVersion = apiVersion.split("/");
|
||||||
|
const group = splitApiVersion[0];
|
||||||
|
|
||||||
|
return this.groupKinds.get(group)?.has(kind) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategoryForEntity<T extends CatalogCategory>(data: CatalogEntityData & CatalogEntityKindData): T | undefined {
|
||||||
|
const splitApiVersion = data.apiVersion.split("/");
|
||||||
|
const group = splitApiVersion[0];
|
||||||
|
|
||||||
|
return this.getForGroupKind(group, data.kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByName(name: string) {
|
||||||
|
return this.items.find(category => category.metadata?.name == name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new filter to the set of category filters
|
||||||
|
* @param fn The function that should return a truthy value if that category should be displayed
|
||||||
|
* @returns A function to remove that filter
|
||||||
|
*/
|
||||||
|
addCatalogCategoryFilter(fn: CategoryFilter): Disposer {
|
||||||
|
this.filters.add(fn);
|
||||||
|
|
||||||
|
return once(() => void this.filters.delete(fn));
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/common/catalog/has-category-for-entity.injectable.ts
Normal file
20
src/common/catalog/has-category-for-entity.injectable.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 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 { CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
|
||||||
|
import catalogCategoryRegistryInjectable from "./category-registry.injectable";
|
||||||
|
|
||||||
|
export type HasCategoryForEntity = (data: CatalogEntityData & CatalogEntityKindData) => boolean;
|
||||||
|
|
||||||
|
const hasCategoryForEntityInjectable = getInjectable({
|
||||||
|
id: "has-category-for-entity",
|
||||||
|
instantiate: (di): HasCategoryForEntity => {
|
||||||
|
const registry = di.inject(catalogCategoryRegistryInjectable);
|
||||||
|
|
||||||
|
return (data) => registry.hasCategoryForEntity(data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default hasCategoryForEntityInjectable;
|
||||||
@ -4,4 +4,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from "./catalog-category-registry";
|
export * from "./catalog-category-registry";
|
||||||
|
export * from "./category-registry";
|
||||||
export * from "./catalog-entity";
|
export * from "./catalog-entity";
|
||||||
|
|||||||
23
src/common/catalog/visit-entity-context-menu.injectable.ts
Normal file
23
src/common/catalog/visit-entity-context-menu.injectable.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 { CatalogEntity, CatalogEntityContextMenuContext } from "./catalog-entity";
|
||||||
|
import catalogCategoryRegistryInjectable from "./category-registry.injectable";
|
||||||
|
|
||||||
|
export type VisitEntityContextMenu = (entity: CatalogEntity, context: CatalogEntityContextMenuContext) => void;
|
||||||
|
|
||||||
|
const visitEntityContextMenuInjectable = getInjectable({
|
||||||
|
id: "visit-entity-context-menu",
|
||||||
|
instantiate: (di): VisitEntityContextMenu => {
|
||||||
|
const categoryRegistry = di.inject(catalogCategoryRegistryInjectable);
|
||||||
|
|
||||||
|
return (entity, context) => {
|
||||||
|
entity.onContextMenuOpen?.(context);
|
||||||
|
categoryRegistry.getCategoryForEntity(entity)?.emit("contextMenuOpen", entity, context);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default visitEntityContextMenuInjectable;
|
||||||
@ -12,7 +12,7 @@ const allowedResourcesInjectable = getInjectable({
|
|||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const cluster = di.inject(hostedClusterInjectable);
|
const cluster = di.inject(hostedClusterInjectable);
|
||||||
|
|
||||||
return computed(() => new Set(cluster.allowedResources), {
|
return computed(() => new Set(cluster?.allowedResources), {
|
||||||
// This needs to be here so that during refresh changes are only propogated when necessary
|
// This needs to be here so that during refresh changes are only propogated when necessary
|
||||||
equals: (cur, prev) => comparer.structural(cur, prev),
|
equals: (cur, prev) => comparer.structural(cur, prev),
|
||||||
});
|
});
|
||||||
|
|||||||
@ -103,8 +103,12 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
return this.clusters.size > 0;
|
return this.clusters.size > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: ClusterId): Cluster | null {
|
getById(id: ClusterId | undefined): Cluster | undefined {
|
||||||
return this.clusters.get(id) ?? null;
|
if (id) {
|
||||||
|
return this.clusters.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
addCluster(clusterOrModel: ClusterModel | Cluster): Cluster {
|
addCluster(clusterOrModel: ClusterModel | Cluster): Cluster {
|
||||||
|
|||||||
@ -3,13 +3,12 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { daemonSetStore } from "./daemonsets.store";
|
import { getClusterIdFromHost } from "../utils";
|
||||||
|
|
||||||
const daemonsetsStoreInjectable = getInjectable({
|
const hostedClusterIdInjectable = getInjectable({
|
||||||
id: "daemonsets-store",
|
id: "hosted-cluster-id",
|
||||||
instantiate: () => daemonSetStore,
|
instantiate: () => getClusterIdFromHost(location.host),
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default daemonsetsStoreInjectable;
|
export default hostedClusterIdInjectable;
|
||||||
|
|
||||||
@ -3,16 +3,17 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { getHostedClusterId } from "../utils";
|
import hostedClusterIdInjectable from "./hosted-cluster-id.injectable";
|
||||||
import clusterStoreInjectable from "./cluster-store.injectable";
|
import clusterStoreInjectable from "./cluster-store.injectable";
|
||||||
|
|
||||||
const hostedClusterInjectable = getInjectable({
|
const hostedClusterInjectable = getInjectable({
|
||||||
id: "hosted-cluster",
|
id: "hosted-cluster",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const hostedClusterId = getHostedClusterId();
|
const hostedClusterId = di.inject(hostedClusterIdInjectable);
|
||||||
|
const store = di.inject(clusterStoreInjectable);
|
||||||
|
|
||||||
return di.inject(clusterStoreInjectable).getById(hostedClusterId);
|
return store.getById(hostedClusterId);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,9 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ipcMain } from "electron";
|
|
||||||
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
|
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
|
||||||
import { broadcastMessage } from "../ipc";
|
import { broadcastMessage } from "../ipc";
|
||||||
import type { ContextHandler } from "../../main/context-handler/context-handler";
|
import type { ClusterContextHandler } from "../../main/context-handler/context-handler";
|
||||||
import type { KubeConfig } from "@kubernetes/client-node";
|
import type { KubeConfig } from "@kubernetes/client-node";
|
||||||
import { HttpError } from "@kubernetes/client-node";
|
import { HttpError } from "@kubernetes/client-node";
|
||||||
import type { Kubectl } from "../../main/kubectl/kubectl";
|
import type { Kubectl } from "../../main/kubectl/kubectl";
|
||||||
@ -14,22 +13,24 @@ import type { KubeconfigManager } from "../../main/kubeconfig-manager/kubeconfig
|
|||||||
import { loadConfigFromFile, loadConfigFromFileSync, validateKubeConfig } from "../kube-helpers";
|
import { loadConfigFromFile, loadConfigFromFileSync, validateKubeConfig } from "../kube-helpers";
|
||||||
import type { KubeApiResource, KubeResource } from "../rbac";
|
import type { KubeApiResource, KubeResource } from "../rbac";
|
||||||
import { apiResourceRecord, apiResources } from "../rbac";
|
import { apiResourceRecord, apiResources } from "../rbac";
|
||||||
import logger from "../../main/logger";
|
|
||||||
import { VersionDetector } from "../../main/cluster-detectors/version-detector";
|
import { VersionDetector } from "../../main/cluster-detectors/version-detector";
|
||||||
import { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
|
import { DetectorRegistry } from "../../main/cluster-detectors/detector-registry";
|
||||||
import plimit from "p-limit";
|
import plimit from "p-limit";
|
||||||
import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate } from "../cluster-types";
|
import type { ClusterState, ClusterRefreshOptions, ClusterMetricsResourceType, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, KubeAuthUpdate } from "../cluster-types";
|
||||||
import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../cluster-types";
|
import { ClusterMetadataKey, initialNodeShellImage, ClusterStatus } from "../cluster-types";
|
||||||
import { disposer, toJS } from "../utils";
|
import { disposer, isDefined, isRequestError, toJS } from "../utils";
|
||||||
import type { Response } from "request";
|
import type { Response } from "request";
|
||||||
import { clusterListNamespaceForbiddenChannel } from "../ipc/cluster";
|
import { clusterListNamespaceForbiddenChannel } from "../ipc/cluster";
|
||||||
import type { CanI } from "./authorization-review.injectable";
|
import type { CanI } from "./authorization-review.injectable";
|
||||||
import type { ListNamespaces } from "./list-namespaces.injectable";
|
import type { ListNamespaces } from "./list-namespaces.injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
import type { Logger } from "../logger";
|
||||||
|
|
||||||
export interface ClusterDependencies {
|
export interface ClusterDependencies {
|
||||||
readonly directoryForKubeConfigs: string;
|
readonly directoryForKubeConfigs: string;
|
||||||
createKubeconfigManager: (cluster: Cluster) => KubeconfigManager;
|
readonly logger: Logger;
|
||||||
createContextHandler: (cluster: Cluster) => ContextHandler;
|
createKubeconfigManager: (cluster: Cluster) => KubeconfigManager | undefined;
|
||||||
|
createContextHandler: (cluster: Cluster) => ClusterContextHandler | undefined;
|
||||||
createKubectl: (clusterVersion: string) => Kubectl;
|
createKubectl: (clusterVersion: string) => Kubectl;
|
||||||
createAuthorizationReview: (config: KubeConfig) => CanI;
|
createAuthorizationReview: (config: KubeConfig) => CanI;
|
||||||
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
createListNamespaces: (config: KubeConfig) => ListNamespaces;
|
||||||
@ -43,17 +44,31 @@ export interface ClusterDependencies {
|
|||||||
export class Cluster implements ClusterModel, ClusterState {
|
export class Cluster implements ClusterModel, ClusterState {
|
||||||
/** Unique id for a cluster */
|
/** Unique id for a cluster */
|
||||||
public readonly id: ClusterId;
|
public readonly id: ClusterId;
|
||||||
private kubeCtl: Kubectl;
|
private kubeCtl: Kubectl | undefined;
|
||||||
/**
|
/**
|
||||||
* Context handler
|
* Context handler
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
public contextHandler: ContextHandler;
|
protected readonly _contextHandler: ClusterContextHandler | undefined;
|
||||||
protected proxyKubeconfigManager: KubeconfigManager;
|
protected readonly _proxyKubeconfigManager: KubeconfigManager | undefined;
|
||||||
protected eventsDisposer = disposer();
|
protected readonly eventsDisposer = disposer();
|
||||||
protected activated = false;
|
protected activated = false;
|
||||||
private resourceAccessStatuses: Map<KubeApiResource, boolean> = new Map();
|
private readonly resourceAccessStatuses = new Map<KubeApiResource, boolean>();
|
||||||
|
|
||||||
|
public get contextHandler() {
|
||||||
|
// TODO: remove these once main/renderer are seperate classes
|
||||||
|
assert(this._contextHandler, "contextHandler is only defined in the main environment");
|
||||||
|
|
||||||
|
return this._contextHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get proxyKubeconfigManager() {
|
||||||
|
// TODO: remove these once main/renderer are seperate classes
|
||||||
|
assert(this._proxyKubeconfigManager, "proxyKubeconfigManager is only defined in the main environment");
|
||||||
|
|
||||||
|
return this._proxyKubeconfigManager;
|
||||||
|
}
|
||||||
|
|
||||||
get whenReady() {
|
get whenReady() {
|
||||||
return when(() => this.ready);
|
return when(() => this.ready);
|
||||||
@ -64,21 +79,21 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
*
|
*
|
||||||
* @observable
|
* @observable
|
||||||
*/
|
*/
|
||||||
@observable contextName: string;
|
@observable contextName!: string;
|
||||||
/**
|
/**
|
||||||
* Path to kubeconfig
|
* Path to kubeconfig
|
||||||
*
|
*
|
||||||
* @observable
|
* @observable
|
||||||
*/
|
*/
|
||||||
@observable kubeConfigPath: string;
|
@observable kubeConfigPath!: string;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@observable workspace: string;
|
@observable workspace?: string;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
@observable workspaces: string[];
|
@observable workspaces?: string[];
|
||||||
/**
|
/**
|
||||||
* Kubernetes API server URL
|
* Kubernetes API server URL
|
||||||
*
|
*
|
||||||
@ -215,7 +230,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
* @computed
|
* @computed
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@computed get defaultNamespace(): string {
|
@computed get defaultNamespace(): string | undefined {
|
||||||
return this.preferences.defaultNamespace;
|
return this.preferences.defaultNamespace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,19 +246,24 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
throw validationError;
|
throw validationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.apiUrl = config.getCluster(config.getContextObject(this.contextName).cluster).server;
|
const context = config.getContextObject(this.contextName);
|
||||||
|
|
||||||
if (ipcMain) {
|
assert(context);
|
||||||
// for the time being, until renderer gets its own cluster type
|
|
||||||
this.contextHandler = this.dependencies.createContextHandler(this);
|
|
||||||
this.proxyKubeconfigManager = this.dependencies.createKubeconfigManager(this);
|
|
||||||
|
|
||||||
logger.debug(`[CLUSTER]: Cluster init success`, {
|
const cluster = config.getCluster(context.cluster);
|
||||||
id: this.id,
|
|
||||||
context: this.contextName,
|
assert(cluster);
|
||||||
apiUrl: this.apiUrl,
|
|
||||||
});
|
this.apiUrl = cluster.server;
|
||||||
}
|
|
||||||
|
// for the time being, until renderer gets its own cluster type
|
||||||
|
this._contextHandler = this.dependencies.createContextHandler(this);
|
||||||
|
this._proxyKubeconfigManager = this.dependencies.createKubeconfigManager(this);
|
||||||
|
this.dependencies.logger.debug(`[CLUSTER]: Cluster init success`, {
|
||||||
|
id: this.id,
|
||||||
|
context: this.contextName,
|
||||||
|
apiUrl: this.apiUrl,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -255,6 +275,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
// Note: do not assign ID as that should never be updated
|
// Note: do not assign ID as that should never be updated
|
||||||
|
|
||||||
this.kubeConfigPath = model.kubeConfigPath;
|
this.kubeConfigPath = model.kubeConfigPath;
|
||||||
|
this.contextName = model.contextName;
|
||||||
|
|
||||||
if (model.workspace) {
|
if (model.workspace) {
|
||||||
this.workspace = model.workspace;
|
this.workspace = model.workspace;
|
||||||
@ -264,10 +285,6 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
this.workspaces = model.workspaces;
|
this.workspaces = model.workspaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (model.contextName) {
|
|
||||||
this.contextName = model.contextName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.preferences) {
|
if (model.preferences) {
|
||||||
this.preferences = model.preferences;
|
this.preferences = model.preferences;
|
||||||
}
|
}
|
||||||
@ -289,7 +306,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected bindEvents() {
|
protected bindEvents() {
|
||||||
logger.info(`[CLUSTER]: bind events`, this.getMeta());
|
this.dependencies.logger.info(`[CLUSTER]: bind events`, this.getMeta());
|
||||||
const refreshTimer = setInterval(() => !this.disconnected && this.refresh(), 30000); // every 30s
|
const refreshTimer = setInterval(() => !this.disconnected && this.refresh(), 30000); // every 30s
|
||||||
const refreshMetadataTimer = setInterval(() => !this.disconnected && this.refreshMetadata(), 900000); // every 15 minutes
|
const refreshMetadataTimer = setInterval(() => !this.disconnected && this.refreshMetadata(), 900000); // every 15 minutes
|
||||||
|
|
||||||
@ -310,13 +327,13 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
protected async recreateProxyKubeconfig() {
|
protected async recreateProxyKubeconfig() {
|
||||||
logger.info("[CLUSTER]: Recreating proxy kubeconfig");
|
this.dependencies.logger.info("[CLUSTER]: Recreating proxy kubeconfig");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.proxyKubeconfigManager.clear();
|
await this.proxyKubeconfigManager.clear();
|
||||||
await this.getProxyKubeconfig();
|
await this.getProxyKubeconfig();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[CLUSTER]: failed to recreate proxy kubeconfig`, error);
|
this.dependencies.logger.error(`[CLUSTER]: failed to recreate proxy kubeconfig`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +347,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
return this.pushState();
|
return this.pushState();
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[CLUSTER]: activate`, this.getMeta());
|
this.dependencies.logger.info(`[CLUSTER]: activate`, this.getMeta());
|
||||||
|
|
||||||
if (!this.eventsDisposer.length) {
|
if (!this.eventsDisposer.length) {
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
@ -348,7 +365,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
await this.refreshAccessibility();
|
await this.refreshAccessibility();
|
||||||
// download kubectl in background, so it's not blocking dashboard
|
// download kubectl in background, so it's not blocking dashboard
|
||||||
this.ensureKubectl()
|
this.ensureKubectl()
|
||||||
.catch(error => logger.warn(`[CLUSTER]: failed to download kubectl for clusterId=${this.id}`, error));
|
.catch(error => this.dependencies.logger.warn(`[CLUSTER]: failed to download kubectl for clusterId=${this.id}`, error));
|
||||||
this.broadcastConnectUpdate("Connected, waiting for view to load ...");
|
this.broadcastConnectUpdate("Connected, waiting for view to load ...");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,9 +389,8 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
async reconnect() {
|
async reconnect() {
|
||||||
logger.info(`[CLUSTER]: reconnect`, this.getMeta());
|
this.dependencies.logger.info(`[CLUSTER]: reconnect`, this.getMeta());
|
||||||
this.contextHandler?.stopServer();
|
await this.contextHandler?.restartServer();
|
||||||
await this.contextHandler?.ensureServer();
|
|
||||||
this.disconnected = false;
|
this.disconnected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,10 +399,10 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
*/
|
*/
|
||||||
@action disconnect(): void {
|
@action disconnect(): void {
|
||||||
if (this.disconnected) {
|
if (this.disconnected) {
|
||||||
return void logger.debug("[CLUSTER]: already disconnected", { id: this.id });
|
return void this.dependencies.logger.debug("[CLUSTER]: already disconnected", { id: this.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`[CLUSTER]: disconnecting`, { id: this.id });
|
this.dependencies.logger.info(`[CLUSTER]: disconnecting`, { id: this.id });
|
||||||
this.eventsDisposer();
|
this.eventsDisposer();
|
||||||
this.contextHandler?.stopServer();
|
this.contextHandler?.stopServer();
|
||||||
this.disconnected = true;
|
this.disconnected = true;
|
||||||
@ -397,7 +413,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
this.allowedNamespaces = [];
|
this.allowedNamespaces = [];
|
||||||
this.resourceAccessStatuses.clear();
|
this.resourceAccessStatuses.clear();
|
||||||
this.pushState();
|
this.pushState();
|
||||||
logger.info(`[CLUSTER]: disconnected`, { id: this.id });
|
this.dependencies.logger.info(`[CLUSTER]: disconnected`, { id: this.id });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -406,7 +422,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
async refresh(opts: ClusterRefreshOptions = {}) {
|
async refresh(opts: ClusterRefreshOptions = {}) {
|
||||||
logger.info(`[CLUSTER]: refresh`, this.getMeta());
|
this.dependencies.logger.info(`[CLUSTER]: refresh`, this.getMeta());
|
||||||
await this.refreshConnectionStatus();
|
await this.refreshConnectionStatus();
|
||||||
|
|
||||||
if (this.accessible) {
|
if (this.accessible) {
|
||||||
@ -424,7 +440,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
*/
|
*/
|
||||||
@action
|
@action
|
||||||
async refreshMetadata() {
|
async refreshMetadata() {
|
||||||
logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta());
|
this.dependencies.logger.info(`[CLUSTER]: refreshMetadata`, this.getMeta());
|
||||||
const metadata = await DetectorRegistry.getInstance().detectForCluster(this);
|
const metadata = await DetectorRegistry.getInstance().detectForCluster(this);
|
||||||
const existingMetadata = this.metadata;
|
const existingMetadata = this.metadata;
|
||||||
|
|
||||||
@ -495,11 +511,31 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
|
|
||||||
return ClusterStatus.AccessGranted;
|
return ClusterStatus.AccessGranted;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[CLUSTER]: Failed to connect to "${this.contextName}": ${error}`);
|
this.dependencies.logger.error(`[CLUSTER]: Failed to connect to "${this.contextName}": ${error}`);
|
||||||
|
|
||||||
if (error.statusCode) {
|
if (isRequestError(error)) {
|
||||||
if (error.statusCode >= 400 && error.statusCode < 500) {
|
if (error.statusCode) {
|
||||||
this.broadcastConnectUpdate("Invalid credentials", true);
|
if (error.statusCode >= 400 && error.statusCode < 500) {
|
||||||
|
this.broadcastConnectUpdate("Invalid credentials", true);
|
||||||
|
|
||||||
|
return ClusterStatus.AccessDenied;
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = String(error.error || error.message) || String(error);
|
||||||
|
|
||||||
|
this.broadcastConnectUpdate(message, true);
|
||||||
|
|
||||||
|
return ClusterStatus.Offline;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.failed === true) {
|
||||||
|
if (error.timedOut === true) {
|
||||||
|
this.broadcastConnectUpdate("Connection timed out", true);
|
||||||
|
|
||||||
|
return ClusterStatus.Offline;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.broadcastConnectUpdate("Failed to fetch credentials", true);
|
||||||
|
|
||||||
return ClusterStatus.AccessDenied;
|
return ClusterStatus.AccessDenied;
|
||||||
}
|
}
|
||||||
@ -507,26 +543,10 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
const message = String(error.error || error.message) || String(error);
|
const message = String(error.error || error.message) || String(error);
|
||||||
|
|
||||||
this.broadcastConnectUpdate(message, true);
|
this.broadcastConnectUpdate(message, true);
|
||||||
|
} else {
|
||||||
return ClusterStatus.Offline;
|
this.broadcastConnectUpdate("Unknown error has occurred", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error.failed === true) {
|
|
||||||
if (error.timedOut === true) {
|
|
||||||
this.broadcastConnectUpdate("Connection timed out", true);
|
|
||||||
|
|
||||||
return ClusterStatus.Offline;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.broadcastConnectUpdate("Failed to fetch credentials", true);
|
|
||||||
|
|
||||||
return ClusterStatus.AccessDenied;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = String(error.error || error.message) || String(error);
|
|
||||||
|
|
||||||
this.broadcastConnectUpdate(message, true);
|
|
||||||
|
|
||||||
return ClusterStatus.Offline;
|
return ClusterStatus.Offline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -575,7 +595,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
* @param state cluster state
|
* @param state cluster state
|
||||||
*/
|
*/
|
||||||
pushState(state = this.getState()) {
|
pushState(state = this.getState()) {
|
||||||
logger.silly(`[CLUSTER]: push-state`, state);
|
this.dependencies.logger.silly(`[CLUSTER]: push-state`, state);
|
||||||
broadcastMessage("cluster:state", this.id, state);
|
broadcastMessage("cluster:state", this.id, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,7 +618,7 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
broadcastConnectUpdate(message: string, isError = false): void {
|
broadcastConnectUpdate(message: string, isError = false): void {
|
||||||
const update: KubeAuthUpdate = { message, isError };
|
const update: KubeAuthUpdate = { message, isError };
|
||||||
|
|
||||||
logger.debug(`[CLUSTER]: broadcasting connection update`, { ...update, meta: this.getMeta() });
|
this.dependencies.logger.debug(`[CLUSTER]: broadcasting connection update`, { ...update, meta: this.getMeta() });
|
||||||
broadcastMessage(`cluster:${this.id}:connection-update`, update);
|
broadcastMessage(`cluster:${this.id}:connection-update`, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,12 +633,12 @@ export class Cluster implements ClusterModel, ClusterState {
|
|||||||
return await listNamespaces();
|
return await listNamespaces();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const ctx = proxyConfig.getContextObject(this.contextName);
|
const ctx = proxyConfig.getContextObject(this.contextName);
|
||||||
const namespaceList = [ctx.namespace].filter(Boolean);
|
const namespaceList = [ctx?.namespace].filter(isDefined);
|
||||||
|
|
||||||
if (namespaceList.length === 0 && error instanceof HttpError && error.statusCode === 403) {
|
if (namespaceList.length === 0 && error instanceof HttpError && error.statusCode === 403) {
|
||||||
const { response } = error as HttpError & { response: Response };
|
const { response } = error as HttpError & { response: Response };
|
||||||
|
|
||||||
logger.info("[CLUSTER]: listing namespaces is forbidden, broadcasting", { clusterId: this.id, error: response.body });
|
this.dependencies.logger.info("[CLUSTER]: listing namespaces is forbidden, broadcasting", { clusterId: this.id, error: response.body });
|
||||||
broadcastMessage(clusterListNamespaceForbiddenChannel, this.id);
|
broadcastMessage(clusterListNamespaceForbiddenChannel, this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,5 +6,8 @@ import { getInjectionToken } from "@ogre-tools/injectable";
|
|||||||
import type { ClusterModel } from "../cluster-types";
|
import type { ClusterModel } from "../cluster-types";
|
||||||
import type { Cluster } from "./cluster";
|
import type { Cluster } from "./cluster";
|
||||||
|
|
||||||
export const createClusterInjectionToken =
|
export type CreateCluster = (model: ClusterModel) => Cluster;
|
||||||
getInjectionToken<(model: ClusterModel) => Cluster>({ id: "create-cluster-token" });
|
|
||||||
|
export const createClusterInjectionToken = getInjectionToken<CreateCluster>({
|
||||||
|
id: "create-cluster-token",
|
||||||
|
});
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
import type { KubeConfig } from "@kubernetes/client-node";
|
import type { KubeConfig } from "@kubernetes/client-node";
|
||||||
import { CoreV1Api } from "@kubernetes/client-node";
|
import { CoreV1Api } from "@kubernetes/client-node";
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import { isDefined } from "../utils";
|
||||||
|
|
||||||
export type ListNamespaces = () => Promise<string[]>;
|
export type ListNamespaces = () => Promise<string[]>;
|
||||||
|
|
||||||
@ -14,7 +15,9 @@ export function listNamespaces(config: KubeConfig): ListNamespaces {
|
|||||||
return async () => {
|
return async () => {
|
||||||
const { body: { items }} = await coreApi.listNamespace();
|
const { body: { items }} = await coreApi.listNamespace();
|
||||||
|
|
||||||
return items.map(ns => ns.metadata.name);
|
return items
|
||||||
|
.map(ns => ns.metadata?.name)
|
||||||
|
.filter(isDefined);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,12 +14,9 @@ describe("verify-that-all-routes-have-component", () => {
|
|||||||
it("verify that routes have route component", async () => {
|
it("verify that routes have route component", async () => {
|
||||||
const rendererDi = getDiForUnitTesting({ doGeneralOverrides: true });
|
const rendererDi = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
rendererDi.override(
|
rendererDi.override(clusterStoreInjectable, () => ({
|
||||||
clusterStoreInjectable,
|
getById: () => null,
|
||||||
() => ({ getById: (): null => null } as unknown as ClusterStore),
|
} as unknown as ClusterStore));
|
||||||
);
|
|
||||||
|
|
||||||
await rendererDi.runSetups();
|
|
||||||
|
|
||||||
const routes = rendererDi.injectMany(routeInjectionToken);
|
const routes = rendererDi.injectMany(routeInjectionToken);
|
||||||
const routeComponents = rendererDi.injectMany(
|
const routeComponents = rendererDi.injectMany(
|
||||||
|
|||||||
20
src/common/hotbars/add-hotbar.injectable.ts
Normal file
20
src/common/hotbars/add-hotbar.injectable.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 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 hotbarStoreInjectable from "./store.injectable";
|
||||||
|
import type { CreateHotbarData, CreateHotbarOptions } from "./types";
|
||||||
|
|
||||||
|
export type AddHotbar = (data: CreateHotbarData, opts?: CreateHotbarOptions) => void;
|
||||||
|
|
||||||
|
const addHotbarInjectable = getInjectable({
|
||||||
|
id: "add-hotbar",
|
||||||
|
instantiate: (di): AddHotbar => {
|
||||||
|
const store = di.inject(hotbarStoreInjectable);
|
||||||
|
|
||||||
|
return (data, opts) => store.add(data, opts);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default addHotbarInjectable;
|
||||||
@ -3,8 +3,9 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import catalogCatalogEntityInjectable from "./catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
||||||
import { HotbarStore } from "./hotbar-store";
|
import { HotbarStore } from "./store";
|
||||||
|
import loggerInjectable from "../logger.injectable";
|
||||||
|
|
||||||
const hotbarStoreInjectable = getInjectable({
|
const hotbarStoreInjectable = getInjectable({
|
||||||
id: "hotbar-store",
|
id: "hotbar-store",
|
||||||
@ -14,6 +15,7 @@ const hotbarStoreInjectable = getInjectable({
|
|||||||
|
|
||||||
return HotbarStore.createInstance({
|
return HotbarStore.createInstance({
|
||||||
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
||||||
|
logger: di.inject(loggerInjectable),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -4,22 +4,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { action, comparer, observable, makeObservable, computed } from "mobx";
|
import { action, comparer, observable, makeObservable, computed } from "mobx";
|
||||||
import { BaseStore } from "./base-store";
|
import { BaseStore } from "../base-store";
|
||||||
import migrations from "../migrations/hotbar-store";
|
import migrations from "../../migrations/hotbar-store";
|
||||||
import { toJS } from "./utils";
|
import { toJS } from "../utils";
|
||||||
import type { CatalogEntity } from "./catalog";
|
import type { CatalogEntity } from "../catalog";
|
||||||
import logger from "../main/logger";
|
import { broadcastMessage } from "../ipc";
|
||||||
import { broadcastMessage } from "./ipc";
|
import type { Hotbar, CreateHotbarData, CreateHotbarOptions } from "./types";
|
||||||
import type {
|
import { defaultHotbarCells, getEmptyHotbar } from "./types";
|
||||||
Hotbar,
|
import { hotbarTooManyItemsChannel } from "../ipc/hotbar";
|
||||||
CreateHotbarData,
|
import type { GeneralEntity } from "../catalog-entities";
|
||||||
CreateHotbarOptions } from "./hotbar-types";
|
import type { Logger } from "../logger";
|
||||||
import {
|
import assert from "assert";
|
||||||
defaultHotbarCells,
|
|
||||||
getEmptyHotbar,
|
|
||||||
} from "./hotbar-types";
|
|
||||||
import { hotbarTooManyItemsChannel } from "./ipc/hotbar";
|
|
||||||
import type { GeneralEntity } from "./catalog-entities";
|
|
||||||
|
|
||||||
export interface HotbarStoreModel {
|
export interface HotbarStoreModel {
|
||||||
hotbars: Hotbar[];
|
hotbars: Hotbar[];
|
||||||
@ -27,15 +22,16 @@ export interface HotbarStoreModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
catalogCatalogEntity: GeneralEntity;
|
readonly catalogCatalogEntity: GeneralEntity;
|
||||||
|
readonly logger: Logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||||
readonly displayName = "HotbarStore";
|
readonly displayName = "HotbarStore";
|
||||||
@observable hotbars: Hotbar[] = [];
|
@observable hotbars: Hotbar[] = [];
|
||||||
@observable private _activeHotbarId: string;
|
@observable private _activeHotbarId!: string;
|
||||||
|
|
||||||
constructor(private dependencies: Dependencies) {
|
constructor(private readonly dependencies: Dependencies) {
|
||||||
super({
|
super({
|
||||||
configName: "lens-hotbar-store",
|
configName: "lens-hotbar-store",
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||||
@ -62,7 +58,7 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
this._activeHotbarId = this.hotbars[hotbar].id;
|
this._activeHotbarId = this.hotbars[hotbar].id;
|
||||||
}
|
}
|
||||||
} else if (typeof hotbar === "string") {
|
} else if (typeof hotbar === "string") {
|
||||||
if (this.getById(hotbar)) {
|
if (this.findById(hotbar)) {
|
||||||
this._activeHotbarId = hotbar;
|
this._activeHotbarId = hotbar;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -120,34 +116,35 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
return toJS(model);
|
return toJS(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActive() {
|
getActive(): Hotbar {
|
||||||
return this.getById(this.activeHotbarId);
|
const hotbar = this.findById(this.activeHotbarId);
|
||||||
|
|
||||||
|
assert(hotbar, "There MUST always be an active hotbar");
|
||||||
|
|
||||||
|
return hotbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(name: string) {
|
findByName(name: string) {
|
||||||
return this.hotbars.find((hotbar) => hotbar.name === name);
|
return this.hotbars.find((hotbar) => hotbar.name === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
getById(id: string) {
|
findById(id: string) {
|
||||||
return this.hotbars.find((hotbar) => hotbar.id === id);
|
return this.hotbars.find((hotbar) => hotbar.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
add = action(
|
@action
|
||||||
(
|
add(data: CreateHotbarData, { setActive = false }: CreateHotbarOptions = {}) {
|
||||||
data: CreateHotbarData,
|
const hotbar = getEmptyHotbar(data.name, data.id);
|
||||||
{ setActive = false }: CreateHotbarOptions = {},
|
|
||||||
) => {
|
|
||||||
const hotbar = getEmptyHotbar(data.name, data.id);
|
|
||||||
|
|
||||||
this.hotbars.push(hotbar);
|
this.hotbars.push(hotbar);
|
||||||
|
|
||||||
if (setActive) {
|
if (setActive) {
|
||||||
this._activeHotbarId = hotbar.id;
|
this._activeHotbarId = hotbar.id;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
setHotbarName = action((id: string, name: string) => {
|
@action
|
||||||
|
setHotbarName(id: string, name: string): void {
|
||||||
const index = this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
const index = this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
@ -158,19 +155,18 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.hotbars[index].name = name;
|
this.hotbars[index].name = name;
|
||||||
});
|
}
|
||||||
|
|
||||||
remove = action((hotbar: Hotbar) => {
|
@action
|
||||||
if (this.hotbars.length <= 1) {
|
remove(hotbar: Hotbar) {
|
||||||
throw new Error("Cannot remove the last hotbar");
|
assert(this.hotbars.length >= 2, "Cannot remove the last hotbar");
|
||||||
}
|
|
||||||
|
|
||||||
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
||||||
|
|
||||||
if (this.activeHotbarId === hotbar.id) {
|
if (this.activeHotbarId === hotbar.id) {
|
||||||
this.setActiveHotbar(0);
|
this.setActiveHotbar(0);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
addToHotbar(item: CatalogEntity, cellIndex?: number) {
|
addToHotbar(item: CatalogEntity, cellIndex?: number) {
|
||||||
@ -209,7 +205,7 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
} else if (0 <= cellIndex && cellIndex < hotbar.items.length) {
|
} else if (0 <= cellIndex && cellIndex < hotbar.items.length) {
|
||||||
hotbar.items[cellIndex] = newItem;
|
hotbar.items[cellIndex] = newItem;
|
||||||
} else {
|
} else {
|
||||||
logger.error(
|
this.dependencies.logger.error(
|
||||||
`[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`,
|
`[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`,
|
||||||
{ entityId: uid, hotbarId: hotbar.id, cellIndex },
|
{ entityId: uid, hotbarId: hotbar.id, cellIndex },
|
||||||
);
|
);
|
||||||
@ -246,8 +242,9 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
|
|
||||||
findClosestEmptyIndex(from: number, direction = 1) {
|
findClosestEmptyIndex(from: number, direction = 1) {
|
||||||
let index = from;
|
let index = from;
|
||||||
|
const hotbar = this.getActive();
|
||||||
|
|
||||||
while (this.getActive().items[index] != null) {
|
while (hotbar.items[index] != null) {
|
||||||
index += direction;
|
index += direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,11 +311,9 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const indexInActiveHotbar = this.getActive().items.findIndex(item => item?.entity.uid === entity.getId());
|
||||||
this.getActive().items.findIndex(
|
|
||||||
(item) => item?.entity.uid === entity.getId(),
|
return indexInActiveHotbar >= 0;
|
||||||
) >= 0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayLabel(hotbar: Hotbar): string {
|
getDisplayLabel(hotbar: Hotbar): string {
|
||||||
@ -4,13 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as uuid from "uuid";
|
import * as uuid from "uuid";
|
||||||
import type { Tuple } from "./utils";
|
import type { Tuple } from "../utils";
|
||||||
import { tuple } from "./utils";
|
import { tuple } from "../utils";
|
||||||
|
|
||||||
export interface HotbarItem {
|
export interface HotbarItem {
|
||||||
entity: {
|
entity: {
|
||||||
uid: string;
|
uid: string;
|
||||||
name?: string;
|
name: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
};
|
};
|
||||||
params?: {
|
params?: {
|
||||||
@ -6,5 +6,5 @@ import type { Channel } from "../channel";
|
|||||||
|
|
||||||
export const createChannel = <Message>(name: string): Channel<Message> => ({
|
export const createChannel = <Message>(name: string): Channel<Message> => ({
|
||||||
name,
|
name,
|
||||||
_template: null,
|
_template: null as never,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -53,7 +53,7 @@ describe("type enforced ipc tests", () => {
|
|||||||
const source = new EventEmitter();
|
const source = new EventEmitter();
|
||||||
const listener = () => called += 1;
|
const listener = () => called += 1;
|
||||||
const results = [true, false, true];
|
const results = [true, false, true];
|
||||||
const verifier = (args: unknown[]): args is [] => results.pop();
|
const verifier = (args: unknown[]): args is [] => results.pop() ?? false;
|
||||||
const channel = "foobar";
|
const channel = "foobar";
|
||||||
|
|
||||||
onCorrect({ source, listener, verifier, channel });
|
onCorrect({ source, listener, verifier, channel });
|
||||||
|
|||||||
@ -13,8 +13,6 @@ export interface ItemObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ItemStore<Item extends ItemObject> {
|
export abstract class ItemStore<Item extends ItemObject> {
|
||||||
abstract loadAll(...args: any[]): Promise<void | Item[]>;
|
|
||||||
|
|
||||||
protected defaultSorting = (item: Item) => item.getName();
|
protected defaultSorting = (item: Item) => item.getName();
|
||||||
|
|
||||||
@observable failedLoading = false;
|
@observable failedLoading = false;
|
||||||
@ -44,8 +42,7 @@ export abstract class ItemStore<Item extends ItemObject> {
|
|||||||
return this.items.length;
|
return this.items.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
getByName(name: string, ...args: any[]): Item;
|
getByName(name: string): Item | undefined {
|
||||||
getByName(name: string): Item {
|
|
||||||
return this.items.find(item => item.getName() === name);
|
return this.items.find(item => item.getName() === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +112,6 @@ export abstract class ItemStore<Item extends ItemObject> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async loadItem(...args: any[]): Promise<Item>;
|
|
||||||
@action
|
@action
|
||||||
protected async loadItem(request: () => Promise<Item>, sortItems = true) {
|
protected async loadItem(request: () => Promise<Item>, sortItems = true) {
|
||||||
const item = await Promise.resolve(request()).catch(() => null);
|
const item = await Promise.resolve(request()).catch(() => null);
|
||||||
@ -133,9 +129,9 @@ export abstract class ItemStore<Item extends ItemObject> {
|
|||||||
if (sortItems) items = this.sortItems(items);
|
if (sortItems) items = this.sortItems(items);
|
||||||
this.items.replace(items);
|
this.items.replace(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@ -3,19 +3,32 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ingressStore } from "../../../renderer/components/+network-ingresses/ingress.store";
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
import { apiManager } from "../api-manager";
|
import type { ApiManager } from "../api-manager";
|
||||||
|
import apiManagerInjectable from "../api-manager/manager.injectable";
|
||||||
import { KubeApi } from "../kube-api";
|
import { KubeApi } from "../kube-api";
|
||||||
import { KubeObject } from "../kube-object";
|
import { KubeObject } from "../kube-object";
|
||||||
|
import { KubeObjectStore } from "../kube-object.store";
|
||||||
|
|
||||||
class TestApi extends KubeApi<KubeObject> {
|
class TestApi extends KubeApi<KubeObject> {
|
||||||
|
|
||||||
protected async checkPreferredVersion() {
|
protected async checkPreferredVersion() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TestStore extends KubeObjectStore<KubeObject, TestApi> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
describe("ApiManager", () => {
|
describe("ApiManager", () => {
|
||||||
|
let apiManager: ApiManager;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
apiManager = di.inject(apiManagerInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
describe("registerApi", () => {
|
describe("registerApi", () => {
|
||||||
it("re-register store if apiBase changed", async () => {
|
it("re-register store if apiBase changed", async () => {
|
||||||
const apiBase = "apis/v1/foo";
|
const apiBase = "apis/v1/foo";
|
||||||
@ -23,25 +36,27 @@ describe("ApiManager", () => {
|
|||||||
const kubeApi = new TestApi({
|
const kubeApi = new TestApi({
|
||||||
objectConstructor: KubeObject,
|
objectConstructor: KubeObject,
|
||||||
apiBase,
|
apiBase,
|
||||||
|
kind: "foo",
|
||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
});
|
});
|
||||||
|
const kubeStore = new TestStore(kubeApi);
|
||||||
|
|
||||||
apiManager.registerApi(apiBase, kubeApi);
|
apiManager.registerApi(apiBase, kubeApi);
|
||||||
|
|
||||||
// Define to use test api for ingress store
|
// Define to use test api for ingress store
|
||||||
Object.defineProperty(ingressStore, "api", { value: kubeApi });
|
Object.defineProperty(kubeStore, "api", { value: kubeApi });
|
||||||
apiManager.registerStore(ingressStore, [kubeApi]);
|
apiManager.registerStore(kubeStore, [kubeApi]);
|
||||||
|
|
||||||
// Test that store is returned with original apiBase
|
// Test that store is returned with original apiBase
|
||||||
expect(apiManager.getStore(kubeApi)).toBe(ingressStore);
|
expect(apiManager.getStore(kubeApi)).toBe(kubeStore);
|
||||||
|
|
||||||
// Change apiBase similar as checkPreferredVersion does
|
// Change apiBase similar as checkPreferredVersion does
|
||||||
Object.defineProperty(kubeApi, "apiBase", { value: fallbackApiBase });
|
Object.defineProperty(kubeApi, "apiBase", { value: fallbackApiBase });
|
||||||
apiManager.registerApi(fallbackApiBase, kubeApi);
|
apiManager.registerApi(fallbackApiBase, kubeApi);
|
||||||
|
|
||||||
// Test that store is returned with new apiBase
|
// Test that store is returned with new apiBase
|
||||||
expect(apiManager.getStore(kubeApi)).toBe(ingressStore);
|
expect(apiManager.getStore(kubeApi)).toBe(kubeStore);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -16,8 +16,15 @@ describe("Crds", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "12345",
|
resourceVersion: "12345",
|
||||||
uid: "12345",
|
uid: "12345",
|
||||||
|
selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo",
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
|
group: "foo.bar",
|
||||||
|
names: {
|
||||||
|
kind: "Foo",
|
||||||
|
plural: "foos",
|
||||||
|
},
|
||||||
|
scope: "Namespaced",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
{
|
||||||
name: "123",
|
name: "123",
|
||||||
@ -44,8 +51,15 @@ describe("Crds", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "12345",
|
resourceVersion: "12345",
|
||||||
uid: "12345",
|
uid: "12345",
|
||||||
|
selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo",
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
|
group: "foo.bar",
|
||||||
|
names: {
|
||||||
|
kind: "Foo",
|
||||||
|
plural: "foos",
|
||||||
|
},
|
||||||
|
scope: "Namespaced",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
{
|
||||||
name: "123",
|
name: "123",
|
||||||
@ -72,8 +86,15 @@ describe("Crds", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "12345",
|
resourceVersion: "12345",
|
||||||
uid: "12345",
|
uid: "12345",
|
||||||
|
selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo",
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
|
group: "foo.bar",
|
||||||
|
names: {
|
||||||
|
kind: "Foo",
|
||||||
|
plural: "foos",
|
||||||
|
},
|
||||||
|
scope: "Namespaced",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
{
|
||||||
name: "123",
|
name: "123",
|
||||||
@ -100,8 +121,15 @@ describe("Crds", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "12345",
|
resourceVersion: "12345",
|
||||||
uid: "12345",
|
uid: "12345",
|
||||||
|
selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo",
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
|
group: "foo.bar",
|
||||||
|
names: {
|
||||||
|
kind: "Foo",
|
||||||
|
plural: "foos",
|
||||||
|
},
|
||||||
|
scope: "Namespaced",
|
||||||
version: "abc",
|
version: "abc",
|
||||||
versions: [
|
versions: [
|
||||||
{
|
{
|
||||||
@ -129,6 +157,7 @@ describe("Crds", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "12345",
|
resourceVersion: "12345",
|
||||||
uid: "12345",
|
uid: "12345",
|
||||||
|
selfLink: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions/foo",
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
version: "abc",
|
version: "abc",
|
||||||
|
|||||||
@ -3,31 +3,39 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Deployment, DeploymentApi } from "../endpoints/deployment.api";
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
|
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
|
||||||
|
import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable";
|
||||||
|
import type { DeploymentApi } from "../endpoints/deployment.api";
|
||||||
|
import deploymentApiInjectable from "../endpoints/deployment.api.injectable";
|
||||||
import type { KubeJsonApi } from "../kube-json-api";
|
import type { KubeJsonApi } from "../kube-json-api";
|
||||||
|
|
||||||
class DeploymentApiTest extends DeploymentApi {
|
|
||||||
public setRequest(request: any) {
|
|
||||||
this.request = request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("DeploymentApi", () => {
|
describe("DeploymentApi", () => {
|
||||||
|
let deploymentApi: DeploymentApi;
|
||||||
|
let kubeJsonApi: jest.Mocked<KubeJsonApi>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
kubeJsonApi = {
|
||||||
|
getResponse: jest.fn(),
|
||||||
|
get: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
put: jest.fn(),
|
||||||
|
patch: jest.fn(),
|
||||||
|
del: jest.fn(),
|
||||||
|
} as never;
|
||||||
|
di.override(apiKubeInjectable, () => kubeJsonApi);
|
||||||
|
|
||||||
|
deploymentApi = di.inject(deploymentApiInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
describe("scale", () => {
|
describe("scale", () => {
|
||||||
const requestMock = {
|
|
||||||
patch: () => ({}),
|
|
||||||
} as unknown as KubeJsonApi;
|
|
||||||
|
|
||||||
const sub = new DeploymentApiTest({ objectConstructor: Deployment });
|
|
||||||
|
|
||||||
sub.setRequest(requestMock);
|
|
||||||
|
|
||||||
it("requests Kubernetes API with PATCH verb and correct amount of replicas", () => {
|
it("requests Kubernetes API with PATCH verb and correct amount of replicas", () => {
|
||||||
const patchSpy = jest.spyOn(requestMock, "patch");
|
deploymentApi.scale({ namespace: "default", name: "deployment-1" }, 5);
|
||||||
|
|
||||||
sub.scale({ namespace: "default", name: "deployment-1" }, 5);
|
expect(kubeJsonApi.patch).toHaveBeenCalledWith("/apis/apps/v1/namespaces/default/deployments/deployment-1/scale", {
|
||||||
|
|
||||||
expect(patchSpy).toHaveBeenCalledWith("/apis/apps/v1/namespaces/default/deployments/deployment-1/scale", {
|
|
||||||
data: {
|
data: {
|
||||||
spec: {
|
spec: {
|
||||||
replicas: 5,
|
replicas: 5,
|
||||||
|
|||||||
@ -3,42 +3,26 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EndpointSubset } from "../endpoints";
|
import { formatEndpointSubset } from "../endpoints";
|
||||||
|
|
||||||
describe("endpoint tests", () => {
|
describe("endpoint tests", () => {
|
||||||
describe("EndpointSubset", () => {
|
describe("EndpointSubset", () => {
|
||||||
it.each([
|
it("formatEndpointSubset should be addresses X ports", () => {
|
||||||
4,
|
const formatted = formatEndpointSubset({
|
||||||
false,
|
|
||||||
null,
|
|
||||||
{},
|
|
||||||
[],
|
|
||||||
"ahe",
|
|
||||||
/a/,
|
|
||||||
])("should always initialize fields when given %j", (data: any) => {
|
|
||||||
const sub = new EndpointSubset(data);
|
|
||||||
|
|
||||||
expect(sub.addresses).toStrictEqual([]);
|
|
||||||
expect(sub.notReadyAddresses).toStrictEqual([]);
|
|
||||||
expect(sub.ports).toStrictEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("toString should be addresses X ports", () => {
|
|
||||||
const sub = new EndpointSubset({
|
|
||||||
addresses: [{
|
addresses: [{
|
||||||
ip: "1.1.1.1",
|
ip: "1.1.1.1",
|
||||||
}, {
|
}, {
|
||||||
ip: "1.1.1.2",
|
ip: "1.1.1.2",
|
||||||
}] as any,
|
}],
|
||||||
notReadyAddresses: [],
|
notReadyAddresses: [],
|
||||||
ports: [{
|
ports: [{
|
||||||
port: "81",
|
port: 81,
|
||||||
}, {
|
}, {
|
||||||
port: "82",
|
port: 82,
|
||||||
}] as any,
|
}],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(sub.toString()).toBe("1.1.1.1:81, 1.1.1.1:82, 1.1.1.2:81, 1.1.1.2:82");
|
expect(formatted).toBe("1.1.1.1:81, 1.1.1.1:82, 1.1.1.2:81, 1.1.1.2:82");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,33 +9,33 @@ import { HelmChart } from "../endpoints/helm-charts.api";
|
|||||||
describe("HelmChart tests", () => {
|
describe("HelmChart tests", () => {
|
||||||
describe("HelmChart.create() tests", () => {
|
describe("HelmChart.create() tests", () => {
|
||||||
it("should throw on non-object input", () => {
|
it("should throw on non-object input", () => {
|
||||||
expect(() => HelmChart.create("" as any)).toThrowError('"value" must be of type object');
|
expect(() => HelmChart.create("" as never)).toThrowError('"value" must be of type object');
|
||||||
expect(() => HelmChart.create(1 as any)).toThrowError('"value" must be of type object');
|
expect(() => HelmChart.create(1 as never)).toThrowError('"value" must be of type object');
|
||||||
expect(() => HelmChart.create(false as any)).toThrowError('"value" must be of type object');
|
expect(() => HelmChart.create(false as never)).toThrowError('"value" must be of type object');
|
||||||
expect(() => HelmChart.create([] as any)).toThrowError('"value" must be of type object');
|
expect(() => HelmChart.create([] as never)).toThrowError('"value" must be of type object');
|
||||||
expect(() => HelmChart.create(Symbol() as any)).toThrowError('"value" must be of type object');
|
expect(() => HelmChart.create(Symbol() as never)).toThrowError('"value" must be of type object');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw on missing fields", () => {
|
it("should throw on missing fields", () => {
|
||||||
expect(() => HelmChart.create({} as any)).toThrowError('"apiVersion" is required');
|
expect(() => HelmChart.create({} as never)).toThrowError('"apiVersion" is required');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "!",
|
apiVersion: "!",
|
||||||
} as any)).toThrowError('"name" is required');
|
} as never)).toThrowError('"name" is required');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "!",
|
apiVersion: "!",
|
||||||
name: "!",
|
name: "!",
|
||||||
} as any)).toThrowError('"version" is required');
|
} as never)).toThrowError('"version" is required');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "!",
|
apiVersion: "!",
|
||||||
name: "!",
|
name: "!",
|
||||||
version: "!",
|
version: "!",
|
||||||
} as any)).toThrowError('"repo" is required');
|
} as never)).toThrowError('"repo" is required');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "!",
|
apiVersion: "!",
|
||||||
name: "!",
|
name: "!",
|
||||||
version: "!",
|
version: "!",
|
||||||
repo: "!",
|
repo: "!",
|
||||||
} as any)).toThrowError('"created" is required');
|
} as never)).toThrowError('"created" is required');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw on fields being wrong type", () => {
|
it("should throw on fields being wrong type", () => {
|
||||||
@ -46,7 +46,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "!",
|
repo: "!",
|
||||||
created: "!",
|
created: "!",
|
||||||
digest: "!",
|
digest: "!",
|
||||||
} as any)).toThrowError('"apiVersion" must be a string');
|
} as never)).toThrowError('"apiVersion" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: 1,
|
name: 1,
|
||||||
@ -54,7 +54,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "!",
|
repo: "!",
|
||||||
created: "!",
|
created: "!",
|
||||||
digest: "!",
|
digest: "!",
|
||||||
} as any)).toThrowError('"name" must be a string');
|
} as never)).toThrowError('"name" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "!",
|
apiVersion: "!",
|
||||||
name: "!",
|
name: "!",
|
||||||
@ -62,7 +62,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "!",
|
repo: "!",
|
||||||
created: "!",
|
created: "!",
|
||||||
digest: 1,
|
digest: 1,
|
||||||
} as any)).toThrowError('"digest" must be a string');
|
} as never)).toThrowError('"digest" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "",
|
name: "",
|
||||||
@ -70,7 +70,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "!",
|
repo: "!",
|
||||||
created: "!",
|
created: "!",
|
||||||
digest: "!",
|
digest: "!",
|
||||||
} as any)).toThrowError('"version" must be a string');
|
} as never)).toThrowError('"version" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -78,7 +78,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: 1,
|
repo: 1,
|
||||||
created: "!",
|
created: "!",
|
||||||
digest: "!",
|
digest: "!",
|
||||||
} as any)).toThrowError('"repo" must be a string');
|
} as never)).toThrowError('"repo" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -86,7 +86,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "1",
|
repo: "1",
|
||||||
created: 1,
|
created: 1,
|
||||||
digest: "a",
|
digest: "a",
|
||||||
} as any)).toThrowError('"created" must be a string');
|
} as never)).toThrowError('"created" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -94,7 +94,7 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "1",
|
repo: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
digest: 1,
|
digest: 1,
|
||||||
} as any)).toThrowError('"digest" must be a string');
|
} as never)).toThrowError('"digest" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -103,7 +103,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
kubeVersion: 1,
|
kubeVersion: 1,
|
||||||
} as any)).toThrowError('"kubeVersion" must be a string');
|
} as never)).toThrowError('"kubeVersion" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -112,7 +112,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
description: 1,
|
description: 1,
|
||||||
} as any)).toThrowError('"description" must be a string');
|
} as never)).toThrowError('"description" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -121,7 +121,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
home: 1,
|
home: 1,
|
||||||
} as any)).toThrowError('"home" must be a string');
|
} as never)).toThrowError('"home" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -130,7 +130,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
engine: 1,
|
engine: 1,
|
||||||
} as any)).toThrowError('"engine" must be a string');
|
} as never)).toThrowError('"engine" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -139,7 +139,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
icon: 1,
|
icon: 1,
|
||||||
} as any)).toThrowError('"icon" must be a string');
|
} as never)).toThrowError('"icon" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -148,7 +148,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
appVersion: 1,
|
appVersion: 1,
|
||||||
} as any)).toThrowError('"appVersion" must be a string');
|
} as never)).toThrowError('"appVersion" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -157,7 +157,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
tillerVersion: 1,
|
tillerVersion: 1,
|
||||||
} as any)).toThrowError('"tillerVersion" must be a string');
|
} as never)).toThrowError('"tillerVersion" must be a string');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -166,7 +166,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
deprecated: 1,
|
deprecated: 1,
|
||||||
} as any)).toThrowError('"deprecated" must be a boolean');
|
} as never)).toThrowError('"deprecated" must be a boolean');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -175,7 +175,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
keywords: 1,
|
keywords: 1,
|
||||||
} as any)).toThrowError('"keywords" must be an array');
|
} as never)).toThrowError('"keywords" must be an array');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -184,7 +184,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
sources: 1,
|
sources: 1,
|
||||||
} as any)).toThrowError('"sources" must be an array');
|
} as never)).toThrowError('"sources" must be an array');
|
||||||
expect(() => HelmChart.create({
|
expect(() => HelmChart.create({
|
||||||
apiVersion: "1",
|
apiVersion: "1",
|
||||||
name: "1",
|
name: "1",
|
||||||
@ -193,7 +193,7 @@ describe("HelmChart tests", () => {
|
|||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
maintainers: 1,
|
maintainers: 1,
|
||||||
} as any)).toThrowError('"maintainers" must be an array');
|
} as never)).toThrowError('"maintainers" must be an array');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should filter non-string keywords", () => {
|
it("should filter non-string keywords", () => {
|
||||||
@ -204,10 +204,10 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "1",
|
repo: "1",
|
||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
keywords: [1, "a", false, {}, "b"] as any,
|
keywords: [1, "a", false, {}, "b"] as never,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(chart.keywords).toStrictEqual(["a", "b"]);
|
expect(chart?.keywords).toStrictEqual(["a", "b"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should filter non-string sources", () => {
|
it("should filter non-string sources", () => {
|
||||||
@ -218,10 +218,10 @@ describe("HelmChart tests", () => {
|
|||||||
repo: "1",
|
repo: "1",
|
||||||
digest: "1",
|
digest: "1",
|
||||||
created: "!",
|
created: "!",
|
||||||
sources: [1, "a", false, {}, "b"] as any,
|
sources: [1, "a", false, {}, "b"] as never,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(chart.sources).toStrictEqual(["a", "b"]);
|
expect(chart?.sources).toStrictEqual(["a", "b"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should filter invalid maintainers", () => {
|
it("should filter invalid maintainers", () => {
|
||||||
@ -236,10 +236,10 @@ describe("HelmChart tests", () => {
|
|||||||
name: "a",
|
name: "a",
|
||||||
email: "b",
|
email: "b",
|
||||||
url: "c",
|
url: "c",
|
||||||
}] as any,
|
}] as never,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(chart.maintainers).toStrictEqual([{
|
expect(chart?.maintainers).toStrictEqual([{
|
||||||
name: "a",
|
name: "a",
|
||||||
email: "b",
|
email: "b",
|
||||||
url: "c",
|
url: "c",
|
||||||
@ -261,9 +261,9 @@ describe("HelmChart tests", () => {
|
|||||||
name: "a",
|
name: "a",
|
||||||
email: "b",
|
email: "b",
|
||||||
url: "c",
|
url: "c",
|
||||||
}] as any,
|
}] as never,
|
||||||
"asdjhajksdhadjks": 1,
|
"asdjhajksdhadjks": 1,
|
||||||
} as any);
|
} as never);
|
||||||
|
|
||||||
expect(warnFn).toHaveBeenCalledWith("HelmChart data has unexpected fields", {
|
expect(warnFn).toHaveBeenCalledWith("HelmChart data has unexpected fields", {
|
||||||
original: anyObject(),
|
original: anyObject(),
|
||||||
|
|||||||
@ -14,6 +14,8 @@ describe("computeRuleDeclarations", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "bar",
|
uid: "bar",
|
||||||
|
namespace: "default",
|
||||||
|
selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -21,6 +23,7 @@ describe("computeRuleDeclarations", () => {
|
|||||||
host: "foo.bar",
|
host: "foo.bar",
|
||||||
http: {
|
http: {
|
||||||
paths: [{
|
paths: [{
|
||||||
|
pathType: "Exact",
|
||||||
backend: {
|
backend: {
|
||||||
service: {
|
service: {
|
||||||
name: "my-service",
|
name: "my-service",
|
||||||
@ -44,6 +47,8 @@ describe("computeRuleDeclarations", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "bar",
|
uid: "bar",
|
||||||
|
namespace: "default",
|
||||||
|
selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,6 +60,7 @@ describe("computeRuleDeclarations", () => {
|
|||||||
host: "foo.bar",
|
host: "foo.bar",
|
||||||
http: {
|
http: {
|
||||||
paths: [{
|
paths: [{
|
||||||
|
pathType: "Exact",
|
||||||
backend: {
|
backend: {
|
||||||
service: {
|
service: {
|
||||||
name: "my-service",
|
name: "my-service",
|
||||||
@ -78,6 +84,8 @@ describe("computeRuleDeclarations", () => {
|
|||||||
name: "foo",
|
name: "foo",
|
||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "bar",
|
uid: "bar",
|
||||||
|
namespace: "default",
|
||||||
|
selfLink: "/apis/networking.k8s.io/v1/ingresses/default/foo",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -91,6 +99,7 @@ describe("computeRuleDeclarations", () => {
|
|||||||
host: "foo.bar",
|
host: "foo.bar",
|
||||||
http: {
|
http: {
|
||||||
paths: [{
|
paths: [{
|
||||||
|
pathType: "Exact",
|
||||||
backend: {
|
backend: {
|
||||||
service: {
|
service: {
|
||||||
name: "my-service",
|
name: "my-service",
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { parseKubeApi } from "../kube-api-parse";
|
|||||||
/**
|
/**
|
||||||
* [<input-url>, <expected-result>]
|
* [<input-url>, <expected-result>]
|
||||||
*/
|
*/
|
||||||
type KubeApiParseTestData = [string, Required<IKubeApiParsed>];
|
type KubeApiParseTestData = [string, IKubeApiParsed];
|
||||||
|
|
||||||
const tests: KubeApiParseTestData[] = [
|
const tests: KubeApiParseTestData[] = [
|
||||||
["/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/prometheuses.monitoring.coreos.com", {
|
["/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/prometheuses.monitoring.coreos.com", {
|
||||||
@ -126,6 +126,6 @@ describe("parseApi unit tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it.each(throwtests)("testing %j should throw", (url) => {
|
it.each(throwtests)("testing %j should throw", (url) => {
|
||||||
expect(() => parseKubeApi(url)).toThrowError("invalid apiPath");
|
expect(() => parseKubeApi(url as never)).toThrowError("invalid apiPath");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,34 +3,36 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request } from "node-fetch";
|
|
||||||
import { forRemoteCluster, KubeApi } from "../kube-api";
|
import { forRemoteCluster, KubeApi } from "../kube-api";
|
||||||
import { KubeJsonApi } from "../kube-json-api";
|
import { KubeJsonApi } from "../kube-json-api";
|
||||||
import { KubeObject } from "../kube-object";
|
import { KubeObject } from "../kube-object";
|
||||||
import AbortController from "abort-controller";
|
import AbortController from "abort-controller";
|
||||||
import { delay } from "../../utils/delay";
|
import { delay } from "../../utils/delay";
|
||||||
import { PassThrough } from "stream";
|
import { PassThrough } from "stream";
|
||||||
import type { ApiManager } from "../api-manager";
|
import { ApiManager } from "../api-manager";
|
||||||
import { apiManager } from "../api-manager";
|
import type { FetchMock } from "jest-fetch-mock/types";
|
||||||
import { Ingress, Pod } from "../endpoints";
|
import { DeploymentApi, Ingress, IngressApi, Pod, PodApi } from "../endpoints";
|
||||||
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
|
import apiManagerInjectable from "../api-manager/manager.injectable";
|
||||||
|
import autoRegistrationInjectable from "../api-manager/auto-registration.injectable";
|
||||||
|
|
||||||
jest.mock("../api-manager");
|
jest.mock("../api-manager");
|
||||||
|
|
||||||
const mockApiManager = apiManager as jest.Mocked<ApiManager>;
|
const mockFetch = fetch as FetchMock;
|
||||||
|
|
||||||
class TestKubeObject extends KubeObject {
|
|
||||||
static kind = "Pod";
|
|
||||||
static namespaced = true;
|
|
||||||
static apiBase = "/api/v1/pods";
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestKubeApi extends KubeApi<TestKubeObject> {
|
|
||||||
public async checkPreferredVersion() {
|
|
||||||
return super.checkPreferredVersion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("forRemoteCluster", () => {
|
describe("forRemoteCluster", () => {
|
||||||
|
let apiManager: jest.Mocked<ApiManager>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
await di.runSetups();
|
||||||
|
|
||||||
|
apiManager = new ApiManager() as jest.Mocked<ApiManager>;
|
||||||
|
|
||||||
|
di.override(apiManagerInjectable, () => apiManager);
|
||||||
|
});
|
||||||
|
|
||||||
it("builds api client for KubeObject", async () => {
|
it("builds api client for KubeObject", async () => {
|
||||||
const api = forRemoteCluster({
|
const api = forRemoteCluster({
|
||||||
cluster: {
|
cluster: {
|
||||||
@ -39,7 +41,7 @@ describe("forRemoteCluster", () => {
|
|||||||
user: {
|
user: {
|
||||||
token: "daa",
|
token: "daa",
|
||||||
},
|
},
|
||||||
}, TestKubeObject);
|
}, Pod);
|
||||||
|
|
||||||
expect(api).toBeInstanceOf(KubeApi);
|
expect(api).toBeInstanceOf(KubeApi);
|
||||||
});
|
});
|
||||||
@ -52,9 +54,9 @@ describe("forRemoteCluster", () => {
|
|||||||
user: {
|
user: {
|
||||||
token: "daa",
|
token: "daa",
|
||||||
},
|
},
|
||||||
}, TestKubeObject, TestKubeApi);
|
}, Pod, PodApi);
|
||||||
|
|
||||||
expect(api).toBeInstanceOf(TestKubeApi);
|
expect(api).toBeInstanceOf(PodApi);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calls right api endpoint", async () => {
|
it("calls right api endpoint", async () => {
|
||||||
@ -65,9 +67,9 @@ describe("forRemoteCluster", () => {
|
|||||||
user: {
|
user: {
|
||||||
token: "daa",
|
token: "daa",
|
||||||
},
|
},
|
||||||
}, TestKubeObject);
|
}, Pod);
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: any) => {
|
mockFetch.mockResponse(async (request: any) => {
|
||||||
expect(request.url).toEqual("https://127.0.0.1:6443/api/v1/pods");
|
expect(request.url).toEqual("https://127.0.0.1:6443/api/v1/pods");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -83,22 +85,31 @@ describe("forRemoteCluster", () => {
|
|||||||
|
|
||||||
describe("KubeApi", () => {
|
describe("KubeApi", () => {
|
||||||
let request: KubeJsonApi;
|
let request: KubeJsonApi;
|
||||||
|
let apiManager: jest.Mocked<ApiManager>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
await di.runSetups();
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
request = new KubeJsonApi({
|
request = new KubeJsonApi({
|
||||||
serverAddress: `http://127.0.0.1:9999`,
|
serverAddress: `http://127.0.0.1:9999`,
|
||||||
apiBase: "/api-kube",
|
apiBase: "/api-kube",
|
||||||
});
|
});
|
||||||
|
apiManager = new ApiManager() as jest.Mocked<ApiManager>;
|
||||||
|
|
||||||
|
di.override(apiManagerInjectable, () => apiManager);
|
||||||
|
di.inject(autoRegistrationInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses url from apiBase if apiBase contains the resource", async () => {
|
it("uses url from apiBase if apiBase contains the resource", async () => {
|
||||||
(fetch as any).mockResponse(async (request: any) => {
|
mockFetch.mockResponse(async (request: any) => {
|
||||||
if (request.url === "http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1") {
|
if (request.url === "http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1") {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [{
|
resources: [{
|
||||||
name: "ingresses",
|
name: "ingresses",
|
||||||
}] as any[],
|
}],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
} else if (request.url === "http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1") {
|
} else if (request.url === "http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1") {
|
||||||
@ -107,13 +118,13 @@ describe("KubeApi", () => {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [{
|
resources: [{
|
||||||
name: "ingresses",
|
name: "ingresses",
|
||||||
}] as any[],
|
}],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [] as any[],
|
resources: [],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -121,9 +132,9 @@ describe("KubeApi", () => {
|
|||||||
|
|
||||||
const apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
const apiBase = "/apis/networking.k8s.io/v1/ingresses";
|
||||||
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
||||||
const kubeApi = new KubeApi({
|
const kubeApi = new IngressApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: KubeObject,
|
objectConstructor: Ingress,
|
||||||
apiBase,
|
apiBase,
|
||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
@ -138,11 +149,11 @@ describe("KubeApi", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("uses url from fallbackApiBases if apiBase lacks the resource", async () => {
|
it("uses url from fallbackApiBases if apiBase lacks the resource", async () => {
|
||||||
(fetch as any).mockResponse(async (request: any) => {
|
mockFetch.mockResponse(async (request: any) => {
|
||||||
if (request.url === "http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1") {
|
if (request.url === "http://127.0.0.1:9999/api-kube/apis/networking.k8s.io/v1") {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [] as any[],
|
resources: [],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
} else if (request.url === "http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1") {
|
} else if (request.url === "http://127.0.0.1:9999/api-kube/apis/extensions/v1beta1") {
|
||||||
@ -150,13 +161,13 @@ describe("KubeApi", () => {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [{
|
resources: [{
|
||||||
name: "ingresses",
|
name: "ingresses",
|
||||||
}] as any[],
|
}],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
resources: [] as any[],
|
resources: [],
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -164,9 +175,10 @@ describe("KubeApi", () => {
|
|||||||
|
|
||||||
const apiBase = "apis/networking.k8s.io/v1/ingresses";
|
const apiBase = "apis/networking.k8s.io/v1/ingresses";
|
||||||
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
const fallbackApiBase = "/apis/extensions/v1beta1/ingresses";
|
||||||
const kubeApi = new KubeApi({
|
const kubeApi = new IngressApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: Object.assign(KubeObject, { apiBase }),
|
objectConstructor: Object.assign(KubeObject, { apiBase }),
|
||||||
|
kind: "Ingress",
|
||||||
fallbackApiBases: [fallbackApiBase],
|
fallbackApiBases: [fallbackApiBase],
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
});
|
});
|
||||||
@ -183,107 +195,100 @@ describe("KubeApi", () => {
|
|||||||
it("registers with apiManager if checkPreferredVersion changes apiVersionPreferred", async () => {
|
it("registers with apiManager if checkPreferredVersion changes apiVersionPreferred", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
const api = new TestKubeApi({
|
const api = new IngressApi({
|
||||||
objectConstructor: Ingress,
|
objectConstructor: Ingress,
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
fallbackApiBases: ["/apis/extensions/v1beta1/ingresses"],
|
fallbackApiBases: ["/apis/extensions/v1beta1/ingresses"],
|
||||||
request: {
|
request: {
|
||||||
get: jest.fn()
|
get: jest.fn()
|
||||||
.mockImplementationOnce((path: string) => {
|
.mockImplementation((path: string) => {
|
||||||
expect(path).toBe("/apis/networking.k8s.io/v1");
|
switch (path) {
|
||||||
|
case "/apis/networking.k8s.io/v1":
|
||||||
throw new Error("no");
|
throw new Error("no");
|
||||||
})
|
case "/apis/extensions/v1beta1":
|
||||||
.mockImplementationOnce((path: string) => {
|
return {
|
||||||
expect(path).toBe("/apis/extensions/v1beta1");
|
resources: [
|
||||||
|
{
|
||||||
return {
|
name: "ingresses",
|
||||||
resources: [
|
},
|
||||||
{
|
],
|
||||||
name: "ingresses",
|
};
|
||||||
},
|
case "/apis/extensions":
|
||||||
],
|
return {
|
||||||
};
|
preferredVersion: {
|
||||||
})
|
version: "v1beta1",
|
||||||
.mockImplementationOnce((path: string) => {
|
},
|
||||||
expect(path).toBe("/apis/extensions");
|
};
|
||||||
|
default:
|
||||||
return {
|
throw new Error("unknown path");
|
||||||
preferredVersion: {
|
}
|
||||||
version: "v1beta1",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
} as any,
|
} as Partial<KubeJsonApi> as KubeJsonApi,
|
||||||
});
|
});
|
||||||
|
|
||||||
await api.checkPreferredVersion();
|
await (api as any).checkPreferredVersion();
|
||||||
|
|
||||||
expect(api.apiVersionPreferred).toBe("v1beta1");
|
expect(api.apiVersionPreferred).toBe("v1beta1");
|
||||||
expect(mockApiManager.registerApi).toBeCalledWith("/apis/extensions/v1beta1/ingresses", expect.anything());
|
expect(apiManager.registerApi).toBeCalledWith(api);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("registers with apiManager if checkPreferredVersion changes apiVersionPreferred with non-grouped apis", async () => {
|
it("registers with apiManager if checkPreferredVersion changes apiVersionPreferred with non-grouped apis", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
const api = new TestKubeApi({
|
const api = new PodApi({
|
||||||
objectConstructor: Pod,
|
objectConstructor: Pod,
|
||||||
checkPreferredVersion: true,
|
checkPreferredVersion: true,
|
||||||
fallbackApiBases: ["/api/v1beta1/pods"],
|
fallbackApiBases: ["/api/v1beta1/pods"],
|
||||||
request: {
|
request: {
|
||||||
get: jest.fn()
|
get: jest.fn()
|
||||||
.mockImplementationOnce((path: string) => {
|
.mockImplementation((path: string) => {
|
||||||
expect(path).toBe("/api/v1");
|
switch (path) {
|
||||||
|
case "/api/v1":
|
||||||
throw new Error("no");
|
throw new Error("no");
|
||||||
})
|
case "/api/v1beta1":
|
||||||
.mockImplementationOnce((path: string) => {
|
return {
|
||||||
expect(path).toBe("/api/v1beta1");
|
resources: [
|
||||||
|
{
|
||||||
return {
|
name: "pods",
|
||||||
resources: [
|
},
|
||||||
{
|
],
|
||||||
name: "pods",
|
};
|
||||||
},
|
case "/api":
|
||||||
],
|
return {
|
||||||
};
|
preferredVersion: {
|
||||||
})
|
version: "v1beta1",
|
||||||
.mockImplementationOnce((path: string) => {
|
},
|
||||||
expect(path).toBe("/api");
|
};
|
||||||
|
default:
|
||||||
return {
|
throw new Error("unknown path");
|
||||||
preferredVersion: {
|
}
|
||||||
version: "v1beta1",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
}),
|
||||||
} as any,
|
} as Partial<KubeJsonApi> as KubeJsonApi,
|
||||||
});
|
});
|
||||||
|
|
||||||
await api.checkPreferredVersion();
|
await (api as any).checkPreferredVersion();
|
||||||
|
|
||||||
expect(api.apiVersionPreferred).toBe("v1beta1");
|
expect(api.apiVersionPreferred).toBe("v1beta1");
|
||||||
expect(mockApiManager.registerApi).toBeCalledWith("/api/v1beta1/pods", expect.anything());
|
expect(apiManager.registerApi).toBeCalledWith(api);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("patch", () => {
|
describe("patch", () => {
|
||||||
let api: TestKubeApi;
|
let api: DeploymentApi;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
api = new TestKubeApi({
|
api = new DeploymentApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: TestKubeObject,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends strategic patch by default", async () => {
|
it("sends strategic patch by default", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("PATCH");
|
expect(request.method).toEqual("PATCH");
|
||||||
expect(request.headers.get("content-type")).toMatch("strategic-merge-patch");
|
expect(request.headers.get("content-type")).toMatch("strategic-merge-patch");
|
||||||
expect(request.body.toString()).toEqual(JSON.stringify({ spec: { replicas: 2 }}));
|
expect(request.body?.toString()).toEqual(JSON.stringify({ spec: { replicas: 2 }}));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
@ -296,10 +301,10 @@ describe("KubeApi", () => {
|
|||||||
it("allows to use merge patch", async () => {
|
it("allows to use merge patch", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("PATCH");
|
expect(request.method).toEqual("PATCH");
|
||||||
expect(request.headers.get("content-type")).toMatch("merge-patch");
|
expect(request.headers.get("content-type")).toMatch("merge-patch");
|
||||||
expect(request.body.toString()).toEqual(JSON.stringify({ spec: { replicas: 2 }}));
|
expect(request.body?.toString()).toEqual(JSON.stringify({ spec: { replicas: 2 }}));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
@ -312,10 +317,10 @@ describe("KubeApi", () => {
|
|||||||
it("allows to use json patch", async () => {
|
it("allows to use json patch", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("PATCH");
|
expect(request.method).toEqual("PATCH");
|
||||||
expect(request.headers.get("content-type")).toMatch("json-patch");
|
expect(request.headers.get("content-type")).toMatch("json-patch");
|
||||||
expect(request.body.toString()).toEqual(JSON.stringify([{ op: "replace", path: "/spec/replicas", value: 2 }]));
|
expect(request.body?.toString()).toEqual(JSON.stringify([{ op: "replace", path: "/spec/replicas", value: 2 }]));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
@ -328,10 +333,10 @@ describe("KubeApi", () => {
|
|||||||
it("allows deep partial patch", async () => {
|
it("allows deep partial patch", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("PATCH");
|
expect(request.method).toEqual("PATCH");
|
||||||
expect(request.headers.get("content-type")).toMatch("merge-patch");
|
expect(request.headers.get("content-type")).toMatch("merge-patch");
|
||||||
expect(request.body.toString()).toEqual(JSON.stringify({ metadata: { annotations: { provisioned: "true" }}}));
|
expect(request.body?.toString()).toEqual(JSON.stringify({ metadata: { annotations: { provisioned: "true" }}}));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
});
|
});
|
||||||
@ -345,18 +350,18 @@ describe("KubeApi", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("delete", () => {
|
describe("delete", () => {
|
||||||
let api: TestKubeApi;
|
let api: PodApi;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
api = new TestKubeApi({
|
api = new PodApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: TestKubeObject,
|
objectConstructor: Pod,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends correct request with empty namespace", async () => {
|
it("sends correct request with empty namespace", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("DELETE");
|
expect(request.method).toEqual("DELETE");
|
||||||
expect(request.url).toEqual("http://127.0.0.1:9999/api-kube/api/v1/pods/foo?propagationPolicy=Background");
|
expect(request.url).toEqual("http://127.0.0.1:9999/api-kube/api/v1/pods/foo?propagationPolicy=Background");
|
||||||
|
|
||||||
@ -368,7 +373,7 @@ describe("KubeApi", () => {
|
|||||||
|
|
||||||
it("sends correct request without namespace", async () => {
|
it("sends correct request without namespace", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("DELETE");
|
expect(request.method).toEqual("DELETE");
|
||||||
expect(request.url).toEqual("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background");
|
expect(request.url).toEqual("http://127.0.0.1:9999/api-kube/api/v1/namespaces/default/pods/foo?propagationPolicy=Background");
|
||||||
|
|
||||||
@ -380,7 +385,7 @@ describe("KubeApi", () => {
|
|||||||
|
|
||||||
it("sends correct request with namespace", async () => {
|
it("sends correct request with namespace", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("DELETE");
|
expect(request.method).toEqual("DELETE");
|
||||||
expect(request.url).toEqual("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods/foo?propagationPolicy=Background");
|
expect(request.url).toEqual("http://127.0.0.1:9999/api-kube/api/v1/namespaces/kube-system/pods/foo?propagationPolicy=Background");
|
||||||
|
|
||||||
@ -392,7 +397,7 @@ describe("KubeApi", () => {
|
|||||||
|
|
||||||
it("allows to change propagationPolicy", async () => {
|
it("allows to change propagationPolicy", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("DELETE");
|
expect(request.method).toEqual("DELETE");
|
||||||
expect(request.url).toMatch("propagationPolicy=Orphan");
|
expect(request.url).toMatch("propagationPolicy=Orphan");
|
||||||
|
|
||||||
@ -404,13 +409,13 @@ describe("KubeApi", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("watch", () => {
|
describe("watch", () => {
|
||||||
let api: TestKubeApi;
|
let api: PodApi;
|
||||||
let stream: PassThrough;
|
let stream: PassThrough;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
api = new TestKubeApi({
|
api = new PodApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: TestKubeObject,
|
objectConstructor: Pod,
|
||||||
});
|
});
|
||||||
stream = new PassThrough();
|
stream = new PassThrough();
|
||||||
});
|
});
|
||||||
@ -423,9 +428,10 @@ describe("KubeApi", () => {
|
|||||||
it("sends a valid watch request", () => {
|
it("sends a valid watch request", () => {
|
||||||
const spy = jest.spyOn(request, "getResponse");
|
const spy = jest.spyOn(request, "getResponse");
|
||||||
|
|
||||||
(fetch as any).mockResponse(async () => {
|
mockFetch.mockResponse(async () => {
|
||||||
return {
|
return {
|
||||||
body: stream,
|
// needed for https://github.com/jefflau/jest-fetch-mock/issues/218
|
||||||
|
body: stream as unknown as string,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -436,9 +442,10 @@ describe("KubeApi", () => {
|
|||||||
it("sends timeout as a query parameter", async () => {
|
it("sends timeout as a query parameter", async () => {
|
||||||
const spy = jest.spyOn(request, "getResponse");
|
const spy = jest.spyOn(request, "getResponse");
|
||||||
|
|
||||||
(fetch as any).mockResponse(async () => {
|
mockFetch.mockResponse(async () => {
|
||||||
return {
|
return {
|
||||||
body: stream,
|
// needed for https://github.com/jefflau/jest-fetch-mock/issues/218
|
||||||
|
body: stream as unknown as string,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -449,13 +456,14 @@ describe("KubeApi", () => {
|
|||||||
it("aborts watch using abortController", async (done) => {
|
it("aborts watch using abortController", async (done) => {
|
||||||
const spy = jest.spyOn(request, "getResponse");
|
const spy = jest.spyOn(request, "getResponse");
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
(request as any).signal.addEventListener("abort", () => {
|
request.signal.addEventListener("abort", () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
body: stream,
|
// needed for https://github.com/jefflau/jest-fetch-mock/issues/218
|
||||||
|
body: stream as unknown as string,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -478,9 +486,9 @@ describe("KubeApi", () => {
|
|||||||
it("if request ended", (done) => {
|
it("if request ended", (done) => {
|
||||||
const spy = jest.spyOn(request, "getResponse");
|
const spy = jest.spyOn(request, "getResponse");
|
||||||
|
|
||||||
jest.spyOn(stream, "on").mockImplementation((eventName: string, callback: Function) => {
|
jest.spyOn(stream, "on").mockImplementation((event: string | symbol, callback: Function) => {
|
||||||
// End the request in 100ms.
|
// End the request in 100ms.
|
||||||
if (eventName === "end") {
|
if (event === "end") {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
callback();
|
callback();
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -493,8 +501,8 @@ describe("KubeApi", () => {
|
|||||||
jest.spyOn(global, "fetch").mockImplementation(async () => {
|
jest.spyOn(global, "fetch").mockImplementation(async () => {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
body: stream,
|
body: stream as never,
|
||||||
} as any;
|
} as Partial<Response> as Response;
|
||||||
});
|
});
|
||||||
|
|
||||||
api.watch({
|
api.watch({
|
||||||
@ -512,9 +520,10 @@ describe("KubeApi", () => {
|
|||||||
it("if request not closed after timeout", (done) => {
|
it("if request not closed after timeout", (done) => {
|
||||||
const spy = jest.spyOn(request, "getResponse");
|
const spy = jest.spyOn(request, "getResponse");
|
||||||
|
|
||||||
(fetch as any).mockResponse(async () => {
|
mockFetch.mockResponse(async () => {
|
||||||
return {
|
return {
|
||||||
body: stream,
|
// needed for https://github.com/jefflau/jest-fetch-mock/issues/218
|
||||||
|
body: stream as unknown as string,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -536,9 +545,9 @@ describe("KubeApi", () => {
|
|||||||
it("retries only once if request ends and timeout is set", (done) => {
|
it("retries only once if request ends and timeout is set", (done) => {
|
||||||
const spy = jest.spyOn(request, "getResponse");
|
const spy = jest.spyOn(request, "getResponse");
|
||||||
|
|
||||||
jest.spyOn(stream, "on").mockImplementation((eventName: string, callback: Function) => {
|
jest.spyOn(stream, "on").mockImplementation((event: string | symbol, callback: Function) => {
|
||||||
// End the request in 100ms.
|
// End the request in 100ms.
|
||||||
if (eventName === "end") {
|
if (event === "end") {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
callback();
|
callback();
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -551,8 +560,8 @@ describe("KubeApi", () => {
|
|||||||
jest.spyOn(global, "fetch").mockImplementation(async () => {
|
jest.spyOn(global, "fetch").mockImplementation(async () => {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
body: stream,
|
body: stream as never,
|
||||||
} as any;
|
} as Partial<Response> as Response;
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeoutSeconds = 0.5;
|
const timeoutSeconds = 0.5;
|
||||||
@ -577,21 +586,21 @@ describe("KubeApi", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
let api: TestKubeApi;
|
let api: PodApi;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
api = new TestKubeApi({
|
api = new PodApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: TestKubeObject,
|
objectConstructor: Pod,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add kind and apiVersion", async () => {
|
it("should add kind and apiVersion", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("POST");
|
expect(request.method).toEqual("POST");
|
||||||
expect(JSON.parse(request.body.toString())).toEqual({
|
expect(JSON.parse(String(request.body))).toEqual({
|
||||||
kind: "Pod",
|
kind: "Pod",
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -643,9 +652,9 @@ describe("KubeApi", () => {
|
|||||||
it("doesn't override metadata.labels", async () => {
|
it("doesn't override metadata.labels", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("POST");
|
expect(request.method).toEqual("POST");
|
||||||
expect(JSON.parse(request.body.toString())).toEqual({
|
expect(JSON.parse(String(request.body))).toEqual({
|
||||||
kind: "Pod",
|
kind: "Pod",
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
metadata: {
|
metadata: {
|
||||||
@ -674,21 +683,21 @@ describe("KubeApi", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
let api: TestKubeApi;
|
let api: PodApi;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
api = new TestKubeApi({
|
api = new PodApi({
|
||||||
request,
|
request,
|
||||||
objectConstructor: TestKubeObject,
|
objectConstructor: Pod,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't override metadata.labels", async () => {
|
it("doesn't override metadata.labels", async () => {
|
||||||
expect.hasAssertions();
|
expect.hasAssertions();
|
||||||
|
|
||||||
(fetch as any).mockResponse(async (request: Request) => {
|
mockFetch.mockResponse(async request => {
|
||||||
expect(request.method).toEqual("PUT");
|
expect(request.method).toEqual("PUT");
|
||||||
expect(JSON.parse(request.body.toString())).toEqual({
|
expect(JSON.parse(String(request.body))).toEqual({
|
||||||
metadata: {
|
metadata: {
|
||||||
name: "foobar",
|
name: "foobar",
|
||||||
namespace: "default",
|
namespace: "default",
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { Cluster } from "../../cluster/cluster";
|
||||||
import type { ClusterContext } from "../cluster-context";
|
import type { ClusterContext } from "../cluster-context";
|
||||||
import type { KubeApi } from "../kube-api";
|
import type { KubeApi } from "../kube-api";
|
||||||
import { KubeObject } from "../kube-object";
|
import { KubeObject } from "../kube-object";
|
||||||
@ -14,6 +15,7 @@ class FakeKubeObjectStore extends KubeObjectStore<KubeObject> {
|
|||||||
allNamespaces: [],
|
allNamespaces: [],
|
||||||
contextNamespaces: [],
|
contextNamespaces: [],
|
||||||
hasSelectedAll: false,
|
hasSelectedAll: false,
|
||||||
|
cluster: {} as Cluster,
|
||||||
} as ClusterContext;
|
} as ClusterContext;
|
||||||
|
|
||||||
get context() {
|
get context() {
|
||||||
@ -40,6 +42,7 @@ describe("KubeObjectStore", () => {
|
|||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "some-uid",
|
uid: "some-uid",
|
||||||
namespace: "default",
|
namespace: "default",
|
||||||
|
selfLink: "/some/self/link",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const store = new FakeKubeObjectStore(loadItems, {
|
const store = new FakeKubeObjectStore(loadItems, {
|
||||||
@ -73,6 +76,7 @@ describe("KubeObjectStore", () => {
|
|||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "some-uid",
|
uid: "some-uid",
|
||||||
namespace: "default",
|
namespace: "default",
|
||||||
|
selfLink: "/some/self/link",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const objNotInDefaultNamespace = new KubeObject({
|
const objNotInDefaultNamespace = new KubeObject({
|
||||||
@ -83,6 +87,7 @@ describe("KubeObjectStore", () => {
|
|||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "some-uid",
|
uid: "some-uid",
|
||||||
namespace: "not-default",
|
namespace: "not-default",
|
||||||
|
selfLink: "/some/self/link",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const store = new FakeKubeObjectStore(loadItems, {
|
const store = new FakeKubeObjectStore(loadItems, {
|
||||||
@ -115,6 +120,7 @@ describe("KubeObjectStore", () => {
|
|||||||
name: "some-obj-name",
|
name: "some-obj-name",
|
||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "some-uid",
|
uid: "some-uid",
|
||||||
|
selfLink: "/some/self/link",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const clusterScopedObject2 = new KubeObject({
|
const clusterScopedObject2 = new KubeObject({
|
||||||
@ -125,6 +131,7 @@ describe("KubeObjectStore", () => {
|
|||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "some-uid",
|
uid: "some-uid",
|
||||||
namespace: "not-default",
|
namespace: "not-default",
|
||||||
|
selfLink: "/some/self/link",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const store = new FakeKubeObjectStore(loadItems, {
|
const store = new FakeKubeObjectStore(loadItems, {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ describe("Nodes tests", () => {
|
|||||||
name: "bar",
|
name: "bar",
|
||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "bat",
|
uid: "bat",
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ describe("Nodes tests", () => {
|
|||||||
resourceVersion: "1",
|
resourceVersion: "1",
|
||||||
uid: "bat",
|
uid: "bat",
|
||||||
labels: {},
|
labels: {},
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,6 +53,7 @@ describe("Nodes tests", () => {
|
|||||||
"node-role.kubernetes.io/foobar": "bat",
|
"node-role.kubernetes.io/foobar": "bat",
|
||||||
"hellonode-role.kubernetes.io/foobar1": "bat",
|
"hellonode-role.kubernetes.io/foobar1": "bat",
|
||||||
},
|
},
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -69,6 +72,7 @@ describe("Nodes tests", () => {
|
|||||||
"node-role.kubernetes.io/foobar": "bat",
|
"node-role.kubernetes.io/foobar": "bat",
|
||||||
"hellonode-role.kubernetes.io//////foobar1": "bat",
|
"hellonode-role.kubernetes.io//////foobar1": "bat",
|
||||||
},
|
},
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,6 +90,7 @@ describe("Nodes tests", () => {
|
|||||||
labels: {
|
labels: {
|
||||||
"kubernetes.io/role": "master",
|
"kubernetes.io/role": "master",
|
||||||
},
|
},
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -103,6 +108,7 @@ describe("Nodes tests", () => {
|
|||||||
labels: {
|
labels: {
|
||||||
"node.kubernetes.io/role": "master",
|
"node.kubernetes.io/role": "master",
|
||||||
},
|
},
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -122,6 +128,7 @@ describe("Nodes tests", () => {
|
|||||||
"kubernetes.io/role": "master",
|
"kubernetes.io/role": "master",
|
||||||
"node.kubernetes.io/role": "master-v2-max",
|
"node.kubernetes.io/role": "master-v2-max",
|
||||||
},
|
},
|
||||||
|
selfLink: "/api/v1/nodes/bar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,8 @@ describe("Pod tests", () => {
|
|||||||
name: "foobar",
|
name: "foobar",
|
||||||
resourceVersion: "foobar",
|
resourceVersion: "foobar",
|
||||||
uid: "foobar",
|
uid: "foobar",
|
||||||
|
namespace: "default",
|
||||||
|
selfLink: "/api/v1/pods/default/foobar",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import assert from "assert";
|
||||||
|
import type { PodContainer, PodContainerStatus } from "../endpoints";
|
||||||
import { Pod } from "../endpoints";
|
import { Pod } from "../endpoints";
|
||||||
|
|
||||||
interface GetDummyPodOptions {
|
interface GetDummyPodOptions {
|
||||||
@ -12,16 +14,18 @@ interface GetDummyPodOptions {
|
|||||||
initDead?: number;
|
initDead?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDummyPodDefaultOptions(): Required<GetDummyPodOptions> {
|
function getDummyPod(rawOpts: GetDummyPodOptions = {}): Pod {
|
||||||
return {
|
const {
|
||||||
running: 0,
|
running = 0,
|
||||||
dead: 0,
|
dead = 0,
|
||||||
initDead: 0,
|
initDead = 0,
|
||||||
initRunning: 0,
|
initRunning = 0,
|
||||||
};
|
} = rawOpts;
|
||||||
}
|
|
||||||
|
|
||||||
function getDummyPod(opts: GetDummyPodOptions = getDummyPodDefaultOptions()): Pod {
|
const containers: PodContainer[] = [];
|
||||||
|
const initContainers: PodContainer[] = [];
|
||||||
|
const containerStatuses: PodContainerStatus[] = [];
|
||||||
|
const initContainerStatuses: PodContainerStatus[] = [];
|
||||||
const pod = new Pod({
|
const pod = new Pod({
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
kind: "Pod",
|
kind: "Pod",
|
||||||
@ -29,36 +33,35 @@ function getDummyPod(opts: GetDummyPodOptions = getDummyPodDefaultOptions()): Po
|
|||||||
uid: "1",
|
uid: "1",
|
||||||
name: "test",
|
name: "test",
|
||||||
resourceVersion: "v1",
|
resourceVersion: "v1",
|
||||||
selfLink: "http",
|
namespace: "default",
|
||||||
|
selfLink: "/api/v1/pods/default/test",
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
containers,
|
||||||
|
initContainers,
|
||||||
|
serviceAccount: "dummy",
|
||||||
|
serviceAccountName: "dummy",
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
phase: "Running",
|
||||||
|
conditions: [],
|
||||||
|
hostIP: "10.0.0.1",
|
||||||
|
podIP: "10.0.0.1",
|
||||||
|
startTime: "now",
|
||||||
|
containerStatuses,
|
||||||
|
initContainerStatuses,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
pod.spec = {
|
for (let i = 0; i < running; i += 1) {
|
||||||
containers: [],
|
const name = `container_running_${i}`;
|
||||||
initContainers: [],
|
|
||||||
serviceAccount: "dummy",
|
|
||||||
serviceAccountName: "dummy",
|
|
||||||
};
|
|
||||||
|
|
||||||
pod.status = {
|
containers.push({
|
||||||
phase: "Running",
|
|
||||||
conditions: [],
|
|
||||||
hostIP: "10.0.0.1",
|
|
||||||
podIP: "10.0.0.1",
|
|
||||||
startTime: "now",
|
|
||||||
containerStatuses: [],
|
|
||||||
initContainerStatuses: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
for (let i = 0; i < opts.running; i += 1) {
|
|
||||||
const name = `container_r_${i}`;
|
|
||||||
|
|
||||||
pod.spec.containers.push({
|
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imagePullPolicy: "dummy",
|
imagePullPolicy: "dummy",
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
pod.status.containerStatuses.push({
|
containerStatuses.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imageID: "dummy",
|
imageID: "dummy",
|
||||||
name,
|
name,
|
||||||
@ -72,15 +75,15 @@ function getDummyPod(opts: GetDummyPodOptions = getDummyPodDefaultOptions()): Po
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < opts.dead; i += 1) {
|
for (let i = 0; i < dead; i += 1) {
|
||||||
const name = `container_d_${i}`;
|
const name = `container_dead_${i}`;
|
||||||
|
|
||||||
pod.spec.containers.push({
|
containers.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imagePullPolicy: "dummy",
|
imagePullPolicy: "dummy",
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
pod.status.containerStatuses.push({
|
containerStatuses.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imageID: "dummy",
|
imageID: "dummy",
|
||||||
name,
|
name,
|
||||||
@ -97,15 +100,15 @@ function getDummyPod(opts: GetDummyPodOptions = getDummyPodDefaultOptions()): Po
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < opts.initRunning; i += 1) {
|
for (let i = 0; i < initRunning; i += 1) {
|
||||||
const name = `container_ir_${i}`;
|
const name = `container_init-running_${i}`;
|
||||||
|
|
||||||
pod.spec.initContainers.push({
|
initContainers.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imagePullPolicy: "dummy",
|
imagePullPolicy: "dummy",
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
pod.status.initContainerStatuses.push({
|
initContainerStatuses.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imageID: "dummy",
|
imageID: "dummy",
|
||||||
name,
|
name,
|
||||||
@ -119,15 +122,15 @@ function getDummyPod(opts: GetDummyPodOptions = getDummyPodDefaultOptions()): Po
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < opts.initDead; i += 1) {
|
for (let i = 0; i < initDead; i += 1) {
|
||||||
const name = `container_id_${i}`;
|
const name = `container_init-dead_${i}`;
|
||||||
|
|
||||||
pod.spec.initContainers.push({
|
initContainers.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imagePullPolicy: "dummy",
|
imagePullPolicy: "dummy",
|
||||||
name,
|
name,
|
||||||
});
|
});
|
||||||
pod.status.initContainerStatuses.push({
|
initContainerStatuses.push({
|
||||||
image: "dummy",
|
image: "dummy",
|
||||||
imageID: "dummy",
|
imageID: "dummy",
|
||||||
name,
|
name,
|
||||||
@ -173,8 +176,8 @@ describe("Pods", () => {
|
|||||||
|
|
||||||
it("getRunningContainers should return only running and init running", () => {
|
it("getRunningContainers should return only running and init running", () => {
|
||||||
const res = [
|
const res = [
|
||||||
...Array.from(new Array(running), (val, index) => getNamedContainer(`container_r_${index}`)),
|
...Array.from(new Array(running), (val, index) => getNamedContainer(`container_running_${index}`)),
|
||||||
...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_ir_${index}`)),
|
...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_init-running_${index}`)),
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(pod.getRunningContainers()).toStrictEqual(res);
|
expect(pod.getRunningContainers()).toStrictEqual(res);
|
||||||
@ -182,10 +185,10 @@ describe("Pods", () => {
|
|||||||
|
|
||||||
it("getAllContainers should return all containers", () => {
|
it("getAllContainers should return all containers", () => {
|
||||||
const res = [
|
const res = [
|
||||||
...Array.from(new Array(running), (val, index) => getNamedContainer(`container_r_${index}`)),
|
...Array.from(new Array(running), (val, index) => getNamedContainer(`container_running_${index}`)),
|
||||||
...Array.from(new Array(dead), (val, index) => getNamedContainer(`container_d_${index}`)),
|
...Array.from(new Array(dead), (val, index) => getNamedContainer(`container_dead_${index}`)),
|
||||||
...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_ir_${index}`)),
|
...Array.from(new Array(initRunning), (val, index) => getNamedContainer(`container_init-running_${index}`)),
|
||||||
...Array.from(new Array(initDead), (val, index) => getNamedContainer(`container_id_${index}`)),
|
...Array.from(new Array(initDead), (val, index) => getNamedContainer(`container_init-dead_${index}`)),
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(pod.getAllContainers()).toStrictEqual(res);
|
expect(pod.getAllContainers()).toStrictEqual(res);
|
||||||
@ -253,7 +256,7 @@ describe("Pods", () => {
|
|||||||
it("should return true if a condition isn't ready", () => {
|
it("should return true if a condition isn't ready", () => {
|
||||||
const pod = getDummyPod({ running: 1 });
|
const pod = getDummyPod({ running: 1 });
|
||||||
|
|
||||||
pod.status.conditions.push({
|
pod.status?.conditions.push({
|
||||||
type: "Ready",
|
type: "Ready",
|
||||||
status: "foobar",
|
status: "foobar",
|
||||||
lastProbeTime: 1,
|
lastProbeTime: 1,
|
||||||
@ -266,7 +269,7 @@ describe("Pods", () => {
|
|||||||
it("should return false if a condition is non-ready", () => {
|
it("should return false if a condition is non-ready", () => {
|
||||||
const pod = getDummyPod({ running: 1 });
|
const pod = getDummyPod({ running: 1 });
|
||||||
|
|
||||||
pod.status.conditions.push({
|
pod.status?.conditions.push({
|
||||||
type: "dummy",
|
type: "dummy",
|
||||||
status: "foobar",
|
status: "foobar",
|
||||||
lastProbeTime: 1,
|
lastProbeTime: 1,
|
||||||
@ -278,8 +281,11 @@ describe("Pods", () => {
|
|||||||
|
|
||||||
it("should return true if a current container is in a crash loop back off", () => {
|
it("should return true if a current container is in a crash loop back off", () => {
|
||||||
const pod = getDummyPod({ running: 1 });
|
const pod = getDummyPod({ running: 1 });
|
||||||
|
const firstStatus = pod.status?.containerStatuses?.[0];
|
||||||
|
|
||||||
pod.status.containerStatuses[0].state = {
|
assert(firstStatus);
|
||||||
|
|
||||||
|
firstStatus.state = {
|
||||||
waiting: {
|
waiting: {
|
||||||
reason: "CrashLookBackOff",
|
reason: "CrashLookBackOff",
|
||||||
message: "too much foobar",
|
message: "too much foobar",
|
||||||
@ -292,6 +298,8 @@ describe("Pods", () => {
|
|||||||
it("should return true if a current phase isn't running", () => {
|
it("should return true if a current phase isn't running", () => {
|
||||||
const pod = getDummyPod({ running: 1 });
|
const pod = getDummyPod({ running: 1 });
|
||||||
|
|
||||||
|
assert(pod.status);
|
||||||
|
|
||||||
pod.status.phase = "not running";
|
pod.status.phase = "not running";
|
||||||
|
|
||||||
expect(pod.hasIssues()).toStrictEqual(true);
|
expect(pod.hasIssues()).toStrictEqual(true);
|
||||||
@ -300,6 +308,8 @@ describe("Pods", () => {
|
|||||||
it("should return false if a current phase is running", () => {
|
it("should return false if a current phase is running", () => {
|
||||||
const pod = getDummyPod({ running: 1 });
|
const pod = getDummyPod({ running: 1 });
|
||||||
|
|
||||||
|
assert(pod.status);
|
||||||
|
|
||||||
pod.status.phase = "Running";
|
pod.status.phase = "Running";
|
||||||
|
|
||||||
expect(pod.hasIssues()).toStrictEqual(false);
|
expect(pod.hasIssues()).toStrictEqual(false);
|
||||||
|
|||||||
@ -3,31 +3,39 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { StatefulSet, StatefulSetApi } from "../endpoints/stateful-set.api";
|
import storesAndApisCanBeCreatedInjectable from "../../../renderer/stores-apis-can-be-created.injectable";
|
||||||
|
import { getDiForUnitTesting } from "../../../renderer/getDiForUnitTesting";
|
||||||
|
import apiKubeInjectable from "../../../renderer/k8s/api-kube.injectable";
|
||||||
|
import type { StatefulSetApi } from "../endpoints";
|
||||||
|
import statefulSetApiInjectable from "../endpoints/stateful-set.api.injectable";
|
||||||
import type { KubeJsonApi } from "../kube-json-api";
|
import type { KubeJsonApi } from "../kube-json-api";
|
||||||
|
|
||||||
class StatefulSetApiTest extends StatefulSetApi {
|
|
||||||
public setRequest(request: any) {
|
|
||||||
this.request = request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("StatefulSetApi", () => {
|
describe("StatefulSetApi", () => {
|
||||||
|
let statefulSetApi: StatefulSetApi;
|
||||||
|
let kubeJsonApi: jest.Mocked<KubeJsonApi>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
di.override(storesAndApisCanBeCreatedInjectable, () => true);
|
||||||
|
kubeJsonApi = {
|
||||||
|
getResponse: jest.fn(),
|
||||||
|
get: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
put: jest.fn(),
|
||||||
|
patch: jest.fn(),
|
||||||
|
del: jest.fn(),
|
||||||
|
} as never;
|
||||||
|
di.override(apiKubeInjectable, () => kubeJsonApi);
|
||||||
|
|
||||||
|
statefulSetApi = di.inject(statefulSetApiInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
describe("scale", () => {
|
describe("scale", () => {
|
||||||
const requestMock = {
|
|
||||||
patch: () => ({}),
|
|
||||||
} as unknown as KubeJsonApi;
|
|
||||||
|
|
||||||
const sub = new StatefulSetApiTest({ objectConstructor: StatefulSet });
|
|
||||||
|
|
||||||
sub.setRequest(requestMock);
|
|
||||||
|
|
||||||
it("requests Kubernetes API with PATCH verb and correct amount of replicas", () => {
|
it("requests Kubernetes API with PATCH verb and correct amount of replicas", () => {
|
||||||
const patchSpy = jest.spyOn(requestMock, "patch");
|
statefulSetApi.scale({ namespace: "default", name: "statefulset-1" }, 5);
|
||||||
|
|
||||||
sub.scale({ namespace: "default", name: "statefulset-1" }, 5);
|
expect(kubeJsonApi.patch).toHaveBeenCalledWith("/apis/apps/v1/namespaces/default/statefulsets/statefulset-1/scale", {
|
||||||
|
|
||||||
expect(patchSpy).toHaveBeenCalledWith("/apis/apps/v1/namespaces/default/statefulsets/statefulset-1/scale", {
|
|
||||||
data: {
|
data: {
|
||||||
spec: {
|
spec: {
|
||||||
replicas: 5,
|
replicas: 5,
|
||||||
|
|||||||
@ -3,18 +3,12 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { isClusterPageContext } from "../utils";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import { KubeJsonApi } from "./kube-json-api";
|
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
import { apiKubePrefix, isDevelopment } from "../vars";
|
import type { KubeJsonApi } from "./kube-json-api";
|
||||||
|
|
||||||
export const apiKube = isClusterPageContext()
|
export const apiKubeInjectionToken = getInjectionToken<KubeJsonApi>({
|
||||||
? new KubeJsonApi({
|
id: "api-kube-injection-token",
|
||||||
serverAddress: `http://127.0.0.1:${window.location.port}`,
|
});
|
||||||
apiBase: apiKubePrefix,
|
|
||||||
debug: isDevelopment,
|
export const apiKube = asLegacyGlobalForExtensionApi(apiKubeInjectionToken);
|
||||||
}, {
|
|
||||||
headers: {
|
|
||||||
"Host": window.location.host,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
: undefined;
|
|
||||||
|
|||||||
@ -1,123 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { KubeObjectStore } from "./kube-object.store";
|
|
||||||
|
|
||||||
import { action, observable, makeObservable } from "mobx";
|
|
||||||
import { autoBind, iter } from "../utils";
|
|
||||||
import type { KubeApi } from "./kube-api";
|
|
||||||
import type { KubeObject } from "./kube-object";
|
|
||||||
import type { IKubeObjectRef } from "./kube-api-parse";
|
|
||||||
import { parseKubeApi, createKubeApiURL } from "./kube-api-parse";
|
|
||||||
|
|
||||||
export class ApiManager {
|
|
||||||
private apis = observable.map<string, KubeApi<KubeObject>>();
|
|
||||||
private stores = observable.map<string, KubeObjectStore<KubeObject>>();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
makeObservable(this);
|
|
||||||
autoBind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
getApi(pathOrCallback: string | ((api: KubeApi<KubeObject>) => boolean)) {
|
|
||||||
if (typeof pathOrCallback === "string") {
|
|
||||||
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
return iter.find(this.apis.values(), pathOrCallback ?? (() => true));
|
|
||||||
}
|
|
||||||
|
|
||||||
getApiByKind(kind: string, apiVersion: string) {
|
|
||||||
return iter.find(this.apis.values(), api => api.kind === kind && api.apiVersionWithGroup === apiVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerApi<K extends KubeObject>(apiBase: string, api: KubeApi<K>) {
|
|
||||||
if (!api.apiBase) return;
|
|
||||||
|
|
||||||
if (!this.apis.has(apiBase)) {
|
|
||||||
this.stores.forEach((store) => {
|
|
||||||
if (store.api === api) {
|
|
||||||
this.stores.set(apiBase, store);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.apis.set(apiBase, api);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected resolveApi(api?: string | KubeApi<KubeObject>): KubeApi<KubeObject> | undefined {
|
|
||||||
if (!api) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof api === "string") {
|
|
||||||
return this.getApi(api) as KubeApi<KubeObject>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
|
|
||||||
unregisterApi(api: string | KubeApi<KubeObject>) {
|
|
||||||
if (typeof api === "string") this.apis.delete(api);
|
|
||||||
else {
|
|
||||||
const apis = Array.from(this.apis.entries());
|
|
||||||
const entry = apis.find(entry => entry[1] === api);
|
|
||||||
|
|
||||||
if (entry) this.unregisterApi(entry[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
registerStore<K extends KubeObject>(store: KubeObjectStore<K>, apis: KubeApi<K>[] = [store.api]) {
|
|
||||||
apis.filter(Boolean).forEach(api => {
|
|
||||||
if (api.apiBase) this.stores.set(api.apiBase, store);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getStore<S extends KubeObjectStore<KubeObject>>(api: string | KubeApi<KubeObject>): S | undefined {
|
|
||||||
return this.stores.get(this.resolveApi(api)?.apiBase) as S;
|
|
||||||
}
|
|
||||||
|
|
||||||
lookupApiLink(ref: IKubeObjectRef, parentObject?: KubeObject): string {
|
|
||||||
const {
|
|
||||||
kind, apiVersion, name,
|
|
||||||
namespace = parentObject?.getNs(),
|
|
||||||
} = ref;
|
|
||||||
|
|
||||||
if (!kind) return "";
|
|
||||||
|
|
||||||
// search in registered apis by 'kind' & 'apiVersion'
|
|
||||||
const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
|
|
||||||
|
|
||||||
if (api) {
|
|
||||||
return api.getUrl({ namespace, name });
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup api by generated resource link
|
|
||||||
const apiPrefixes = ["/apis", "/api"];
|
|
||||||
const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;
|
|
||||||
|
|
||||||
for (const apiPrefix of apiPrefixes) {
|
|
||||||
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
|
||||||
|
|
||||||
if (this.getApi(apiLink)) {
|
|
||||||
return apiLink;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve by kind only (hpa's might use refs to older versions of resources for example)
|
|
||||||
const apiByKind = this.getApi(api => api.kind === kind);
|
|
||||||
|
|
||||||
if (apiByKind) {
|
|
||||||
return apiByKind.getUrl({ name, namespace });
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise generate link with default prefix
|
|
||||||
// resource still might exists in k8s, but api is not registered in the app
|
|
||||||
return createKubeApiURL({ apiVersion, name, namespace, resource });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const apiManager = new ApiManager();
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user