Lens restructure (#540)

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman authored and GitHub committed 2020-06-30 14:35:16 +03:00
1 parent e810eab39f
commit b7974827d2
786 files changed
+14567 -24066

No files matched your search

-9
View File
@@ -1,9 +0,0 @@
const userStore = {
getPreferences: jest.fn(() => {
return {
downloadMirror: "default"
}
})
}
export { userStore };
-14
View File
@@ -1,14 +0,0 @@
import { app, remote } from "electron"
/**
*
* @returns app version correctly regardless of dev/prod mode and main/renderer differences
*/
export function getAppVersion(): string {
// app is undefined when running in renderer
let version = (app || remote.app).getVersion()
if(process.env.NODE_ENV === 'development') {
version = require("../../package.json").version
}
return version;
}
+18 -15
View File
@@ -1,12 +1,12 @@
import * as ElectronStore from "electron-store"
import ElectronStore from "electron-store"
import { Cluster, ClusterBaseInfo } from "../main/cluster";
import { getAppVersion } from "./app-utils"
import * as version200Beta2 from "./migrations/cluster-store/2.0.0-beta.2"
import * as version241 from "./migrations/cluster-store/2.4.1"
import * as version260Beta2 from "./migrations/cluster-store/2.6.0-beta.2"
import * as version260Beta3 from "./migrations/cluster-store/2.6.0-beta.3"
import * as version270Beta0 from "./migrations/cluster-store/2.7.0-beta.0"
import * as version270Beta1 from "./migrations/cluster-store/2.7.0-beta.1"
import * as version200Beta2 from "../migrations/cluster-store/2.0.0-beta.2"
import * as version241 from "../migrations/cluster-store/2.4.1"
import * as version260Beta2 from "../migrations/cluster-store/2.6.0-beta.2"
import * as version260Beta3 from "../migrations/cluster-store/2.6.0-beta.3"
import * as version270Beta0 from "../migrations/cluster-store/2.7.0-beta.0"
import * as version270Beta1 from "../migrations/cluster-store/2.7.0-beta.1"
import { getAppVersion } from "./utils/app-version";
export class ClusterStore {
private static instance: ClusterStore;
@@ -14,8 +14,10 @@ export class ClusterStore {
private constructor() {
this.store = new ElectronStore({
name: "lens-cluster-store",
// @ts-ignore
// fixme: tests are failed without "projectVersion"
projectVersion: getAppVersion(),
name: "lens-cluster-store",
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
migrations: {
"2.0.0-beta.2": version200Beta2.migration,
@@ -58,7 +60,9 @@ export class ClusterStore {
public getCluster(id: string): Cluster {
const cluster = this.getAllClusterObjects().find((cluster) => cluster.id === id)
if (cluster) { return cluster}
if (cluster) {
return cluster
}
return null
}
@@ -74,7 +78,8 @@ export class ClusterStore {
}
if (index === -1) {
clusters.push(storable)
} else {
}
else {
clusters[index] = storable
}
this.store.set("clusters", clusters)
@@ -97,7 +102,7 @@ export class ClusterStore {
}
static getInstance(): ClusterStore {
if(!ClusterStore.instance) {
if (!ClusterStore.instance) {
ClusterStore.instance = new ClusterStore();
}
return ClusterStore.instance;
@@ -108,6 +113,4 @@ export class ClusterStore {
}
}
const clusterStore: ClusterStore = ClusterStore.getInstance();
export { clusterStore };
export const clusterStore = ClusterStore.getInstance();
+333
View File
@@ -0,0 +1,333 @@
import mockFs from "mock-fs"
import yaml from "js-yaml"
import { ClusterStore } from "./cluster-store";
import { Cluster } from "../main/cluster";
// Console.log needs to be called before fs-mocks, see https://github.com/tschaub/mock-fs/issues/234
console.log("");
describe("for an empty config", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("allows to store and retrieve a cluster", async () => {
const cluster = new Cluster({
id: 'foo',
kubeConfig: 'kubeconfig string',
preferences: {
terminalCWD: '/tmp',
icon: 'path to icon'
}
})
const clusterStore = ClusterStore.getInstance()
clusterStore.storeCluster(cluster);
const storedCluster = clusterStore.getCluster(cluster.id);
expect(storedCluster.kubeConfig).toBe(cluster.kubeConfig)
expect(storedCluster.preferences.icon).toBe(cluster.preferences.icon)
expect(storedCluster.preferences.terminalCWD).toBe(cluster.preferences.terminalCWD)
expect(storedCluster.id).toBe(cluster.id)
})
it("allows to delete a cluster", async () => {
const cluster = new Cluster({
id: 'foofoo',
kubeConfig: 'kubeconfig string',
preferences: {
terminalCWD: '/tmp'
}
})
const clusterStore = ClusterStore.getInstance()
clusterStore.storeCluster(cluster);
const storedCluster = clusterStore.getCluster(cluster.id);
expect(storedCluster.id).toBe(cluster.id)
clusterStore.removeCluster(cluster.id);
expect(clusterStore.getCluster(cluster.id)).toBe(null)
})
})
describe("for a config with existing clusters", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({
__internal__: {
migrations: {
version: "99.99.99"
}
},
clusters: [
{
id: 'cluster1',
kubeConfig: 'foo',
preferences: { terminalCWD: '/foo' }
},
{
id: 'cluster2',
kubeConfig: 'foo2',
preferences: { terminalCWD: '/foo2' }
}
]
})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("allows to retrieve a cluster", async () => {
const clusterStore = ClusterStore.getInstance()
const storedCluster = clusterStore.getCluster('cluster1')
expect(storedCluster.kubeConfig).toBe('foo')
expect(storedCluster.preferences.terminalCWD).toBe('/foo')
expect(storedCluster.id).toBe('cluster1')
const storedCluster2 = clusterStore.getCluster('cluster2')
expect(storedCluster2.kubeConfig).toBe('foo2')
expect(storedCluster2.preferences.terminalCWD).toBe('/foo2')
expect(storedCluster2.id).toBe('cluster2')
})
it("allows to delete a cluster", async () => {
const clusterStore = ClusterStore.getInstance()
clusterStore.removeCluster('cluster2')
// Verify the other cluster still exists:
const storedCluster = clusterStore.getCluster('cluster1')
expect(storedCluster.id).toBe('cluster1')
const storedCluster2 = clusterStore.getCluster('cluster2')
expect(storedCluster2).toBe(null)
})
it("allows to reload a cluster in-place", async () => {
const cluster = new Cluster({
id: 'cluster1',
kubeConfig: 'kubeconfig string',
preferences: {
terminalCWD: '/tmp'
}
})
const clusterStore = ClusterStore.getInstance()
clusterStore.reloadCluster(cluster)
expect(cluster.kubeConfig).toBe('foo')
expect(cluster.preferences.terminalCWD).toBe('/foo')
expect(cluster.id).toBe('cluster1')
})
it("allows getting all the clusters", async () => {
const clusterStore = ClusterStore.getInstance()
const storedClusters = clusterStore.getAllClusters()
expect(storedClusters[0].id).toBe('cluster1')
expect(storedClusters[0].preferences.terminalCWD).toBe('/foo')
expect(storedClusters[0].kubeConfig).toBe('foo')
expect(storedClusters[1].id).toBe('cluster2')
expect(storedClusters[1].preferences.terminalCWD).toBe('/foo2')
expect(storedClusters[1].kubeConfig).toBe('foo2')
})
it("allows storing the clusters in a different order", async () => {
const clusterStore = ClusterStore.getInstance()
const storedClusters = clusterStore.getAllClusters()
const reorderedClusters = [storedClusters[1], storedClusters[0]]
clusterStore.storeClusters(reorderedClusters)
const storedClusters2 = clusterStore.getAllClusters()
expect(storedClusters2[0].id).toBe('cluster2')
expect(storedClusters2[1].id).toBe('cluster1')
})
})
describe("for a pre 2.0 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({
__internal__: {
migrations: {
version: "1.0.0"
}
},
cluster1: 'kubeconfig content'
})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("migrates to modern format with kubeconfig under a key", async () => {
const clusterStore = ClusterStore.getInstance()
const storedCluster = clusterStore.store.get('clusters')[0]
expect(storedCluster.kubeConfig).toBe('kubeconfig content')
})
})
describe("for a pre 2.4.1 config with an existing cluster", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({
__internal__: {
migrations: {
version: "2.0.0-beta.2"
}
},
cluster1: {
kubeConfig: 'foo',
online: true,
accessible: false,
failureReason: 'user error'
},
})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("migrates to modern format throwing out the state related data", async () => {
const clusterStore = ClusterStore.getInstance()
const storedClusterData = clusterStore.store.get('clusters')[0]
expect(storedClusterData.hasOwnProperty('online')).toBe(false)
expect(storedClusterData.hasOwnProperty('accessible')).toBe(false)
expect(storedClusterData.hasOwnProperty('failureReason')).toBe(false)
})
})
describe("for a pre 2.6.0 config with a cluster that has arrays in auth config", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({
__internal__: {
migrations: {
version: "2.4.1"
}
},
cluster1: {
kubeConfig: "apiVersion: v1\nclusters:\n- cluster:\n server: https://10.211.55.6:8443\n name: minikube\ncontexts:\n- context:\n cluster: minikube\n user: minikube\n name: minikube\ncurrent-context: minikube\nkind: Config\npreferences: {}\nusers:\n- name: minikube\n user:\n client-certificate: /Users/kimmo/.minikube/client.crt\n client-key: /Users/kimmo/.minikube/client.key\n auth-provider:\n config:\n access-token:\n - should be string\n expiry:\n - should be string\n"
},
})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("replaces array format access token and expiry into string", async () => {
const clusterStore = ClusterStore.getInstance()
const storedClusterData = clusterStore.store.get('clusters')[0]
const kc = yaml.safeLoad(storedClusterData.kubeConfig)
expect(kc.users[0].user['auth-provider'].config['access-token']).toBe("should be string")
expect(kc.users[0].user['auth-provider'].config['expiry']).toBe("should be string")
})
})
describe("for a pre 2.6.0 config with a cluster icon", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({
__internal__: {
migrations: {
version: "2.4.1"
}
},
cluster1: {
kubeConfig: "foo",
icon: "icon path",
preferences: {
terminalCWD: "/tmp"
}
},
})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("moves the icon into preferences", async () => {
const clusterStore = ClusterStore.getInstance()
const storedClusterData = clusterStore.store.get('clusters')[0]
expect(storedClusterData.hasOwnProperty('icon')).toBe(false)
expect(storedClusterData.preferences.hasOwnProperty('icon')).toBe(true)
expect(storedClusterData.preferences.icon).toBe("icon path")
})
})
describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
beforeEach(() => {
ClusterStore.resetInstance()
const mockOpts = {
'tmp': {
'lens-cluster-store.json': JSON.stringify({
__internal__: {
migrations: {
version: "2.6.6"
}
},
cluster1: {
kubeConfig: "foo",
icon: "icon path",
preferences: {
terminalCWD: "/tmp"
}
},
})
}
}
mockFs(mockOpts)
})
afterEach(() => {
mockFs.restore()
})
it("adds cluster to default workspace", async () => {
const clusterStore = ClusterStore.getInstance()
const storedClusterData = clusterStore.store.get("clusters")[0]
expect(storedClusterData.workspace).toBe('default')
})
})
@@ -1,13 +0,0 @@
/* Early store format had the kubeconfig directly under context name, this moves
it under the kubeConfig key */
export function migration(store: any) {
console.log("CLUSTER STORE, MIGRATION: 2.0.0-beta.2");
for (const value of store) {
const contextName = value[0];
// Looping all the keys gives out the store internal stuff too...
if(contextName === "__internal__" || value[1].hasOwnProperty('kubeConfig')) continue;
store.set(contextName, { kubeConfig: value[1] });
}
}
@@ -1,11 +0,0 @@
// Cleans up a store that had the state related data stored
export function migration(store: any) {
console.log("CLUSTER STORE, MIGRATION: 2.4.1");
for (const value of store) {
const contextName = value[0];
if(contextName === "__internal__") continue;
const cluster = value[1];
store.set(contextName, { kubeConfig: cluster.kubeConfig, icon: cluster.icon || null, preferences: cluster.preferences || {} });
}
}
@@ -1,15 +0,0 @@
// Move cluster icon from root to preferences
export function migration(store: any) {
console.log("CLUSTER STORE, MIGRATION: 2.6.0-beta.2");
for (const value of store) {
const clusterKey = value[0];
if(clusterKey === "__internal__") continue
const cluster = value[1];
if(!cluster.preferences) cluster.preferences = {};
if(cluster.icon) {
cluster.preferences.icon = cluster.icon;
delete(cluster["icon"]);
}
store.set(clusterKey, { contextName: clusterKey, kubeConfig: value[1].kubeConfig, preferences: value[1].preferences });
}
}
@@ -1,35 +0,0 @@
import * as yaml from "js-yaml"
// Convert access token and expiry from arrays into strings
export function migration(store: any) {
console.log("CLUSTER STORE, MIGRATION: 2.6.0-beta.3");
for (const value of store) {
const clusterKey = value[0];
if(clusterKey === "__internal__") continue
const cluster = value[1];
if(!cluster.kubeConfig) continue
const kubeConfig = yaml.safeLoad(cluster.kubeConfig)
if(!kubeConfig.hasOwnProperty('users')) continue
const userObj = kubeConfig.users[0]
if (userObj) {
const user = userObj.user
if (user["auth-provider"] && user["auth-provider"].config) {
const authConfig = user["auth-provider"].config
if (authConfig["access-token"]) {
authConfig["access-token"] = `${authConfig["access-token"]}`
}
if (authConfig.expiry) {
authConfig.expiry = `${authConfig.expiry}`
}
console.log(authConfig)
user["auth-provider"].config = authConfig
kubeConfig.users = [{
name: userObj.name,
user: user
}]
cluster.kubeConfig = yaml.safeDump(kubeConfig)
store.set(clusterKey, cluster)
}
}
}
}
@@ -1,11 +0,0 @@
// Add existing clusters to "default" workspace
export function migration(store: any) {
console.log("CLUSTER STORE, MIGRATION: 2.7.0-beta.0");
for (const value of store) {
const clusterKey = value[0];
if(clusterKey === "__internal__") continue
const cluster = value[1];
cluster.workspace = "default"
store.set(clusterKey, cluster)
}
}
@@ -1,22 +0,0 @@
// add id for clusters and store them to array
import { v4 as uuid } from "uuid"
export function migration(store: any) {
console.log("CLUSTER STORE, MIGRATION: 2.7.0-beta.1");
const clusters: any[] = []
for (const value of store) {
const clusterKey = value[0];
if(clusterKey === "__internal__") continue
if(clusterKey === "clusters") continue
const cluster = value[1];
cluster.id = uuid()
if (!cluster.preferences.clusterName) {
cluster.preferences.clusterName = clusterKey
}
clusters.push(cluster)
store.delete(clusterKey)
}
if (clusters.length > 0) {
store.set("clusters", clusters)
}
}
@@ -1,4 +0,0 @@
// Add / reset "lastSeenAppVersion"
export function migration(store: any) {
store.set("lastSeenAppVersion", "0.0.0");
}
+25
View File
@@ -0,0 +1,25 @@
// Setup static folder for common assets
import path from "path";
import { protocol } from "electron"
import logger from "../main/logger";
import { staticDir, staticProto, outDir } from "./vars";
export function registerStaticProtocol(rootFolder = staticDir) {
const scheme = staticProto.replace("://", "");
protocol.registerFileProtocol(scheme, (request, callback) => {
const relativePath = request.url.replace(staticProto, "");
const absPath = path.resolve(rootFolder, relativePath);
callback(absPath);
}, (error) => {
logger.debug(`Failed to register protocol "${scheme}"`, error);
})
}
export function getStaticUrl(filePath: string) {
return staticProto + filePath;
}
export function getStaticPath(filePath: string) {
return path.resolve(staticDir, filePath);
}
+2 -2
View File
@@ -1,5 +1,5 @@
import * as request from "request"
import { userStore } from "../common/user-store"
import request from "request"
import { userStore } from "./user-store"
export function globalRequestOpts(requestOpts: request.Options ) {
const userPrefs = userStore.getPreferences()
+11 -3
View File
@@ -1,6 +1,14 @@
import * as winca from "win-ca/api"
import "mac-ca"
import { isMac, isWindows } from "./vars";
import winca from "win-ca"
import macca from "mac-ca"
import logger from "../main/logger"
if (process.platform === "win32") {
if (isMac) {
for (const crt of macca.all()) {
const attributes = crt.issuer?.attributes?.map((a: any) => `${a.name}=${a.value}`)
logger.debug("Using host CA: " + attributes.join(","))
}
}
if (isWindows) {
winca.inject("+") // see: https://github.com/ukoloff/win-ca#caveats
}
+3 -3
View File
@@ -1,6 +1,6 @@
import { machineIdSync } from 'node-machine-id'
import { userStore } from "../common/user-store"
import * as ua from "universal-analytics"
import ua from "universal-analytics"
import { machineIdSync } from "node-machine-id"
import { userStore } from "./user-store"
const GA_ID = "UA-159377374-1"
+7 -5
View File
@@ -1,6 +1,6 @@
import * as ElectronStore from "electron-store"
import * as appUtil from "./app-utils"
import * as version210Beta4 from "./migrations/user-store/2.1.0-beta.4"
import ElectronStore from "electron-store"
import * as version210Beta4 from "../migrations/user-store/2.1.0-beta.4"
import { getAppVersion } from "./utils/app-version";
export interface User {
id?: string;
@@ -20,7 +20,9 @@ export class UserStore {
private constructor() {
this.store = new ElectronStore({
projectVersion: appUtil.getAppVersion(),
// @ts-ignore
// fixme: tests are failed without "projectVersion"
projectVersion: getAppVersion(),
migrations: {
"2.1.0-beta.4": version210Beta4.migration,
}
@@ -68,7 +70,7 @@ export class UserStore {
}
static getInstance(): UserStore {
if(!UserStore.instance) {
if (!UserStore.instance) {
UserStore.instance = new UserStore();
}
return UserStore.instance;
+75
View File
@@ -0,0 +1,75 @@
import mockFs from "mock-fs"
import { userStore, UserStore } from "./user-store"
// Console.log needs to be called before fs-mocks, see https://github.com/tschaub/mock-fs/issues/234
console.log("");
describe("for an empty config", () => {
beforeEach(() => {
UserStore.resetInstance()
const mockOpts = {
'tmp': {
'config.json': JSON.stringify({})
}
}
mockFs(mockOpts)
const userStore = UserStore.getInstance()
})
afterEach(() => {
mockFs.restore()
})
it("allows setting and retrieving lastSeenAppVersion", async () => {
userStore.setLastSeenAppVersion("1.2.3");
expect(userStore.lastSeenAppVersion()).toBe("1.2.3");
})
it("allows adding and listing seen contexts", async () => {
userStore.storeSeenContext(['foo'])
expect(userStore.getSeenContexts().length).toBe(1)
userStore.storeSeenContext(['foo', 'bar'])
const seenContexts = userStore.getSeenContexts()
expect(seenContexts.length).toBe(2) // check 'foo' isn't added twice
expect(seenContexts[0]).toBe('foo')
expect(seenContexts[1]).toBe('bar')
})
it("allows setting and getting preferences", async () => {
userStore.setPreferences({
httpsProxy: 'abcd://defg',
})
const storedPreferences = userStore.getPreferences()
expect(storedPreferences.httpsProxy).toBe('abcd://defg')
expect(storedPreferences.colorTheme).toBe('dark') // defaults to dark
userStore.setPreferences({
colorTheme: 'light'
})
expect(userStore.getPreferences().colorTheme).toBe('light')
})
})
describe("migrations", () => {
beforeEach(() => {
UserStore.resetInstance()
const mockOpts = {
'tmp': {
'config.json': JSON.stringify({
user: { username: 'foobar' },
preferences: { colorTheme: 'light' },
lastSeenAppVersion: '1.2.3'
})
}
}
mockFs(mockOpts)
const userStore = UserStore.getInstance()
})
afterEach(() => {
mockFs.restore()
})
it("sets last seen app version to 0.0.0", async () => {
expect(userStore.lastSeenAppVersion()).toBe('0.0.0')
})
})
+9
View File
@@ -0,0 +1,9 @@
import packageInfo from "../../../package.json"
export function getAppVersion(): string {
return packageInfo.version;
}
export function getBundledKubectlVersion(): string {
return packageInfo.config.bundledKubectlVersion;
}
+12
View File
@@ -0,0 +1,12 @@
// Encode/decode utf-8 base64 string
import * as Base64 from "crypto-js/enc-base64";
import * as Utf8 from "crypto-js/enc-utf8";
export const base64 = {
decode(data: string) {
return Base64.parse(data).toString(Utf8);
},
encode(data: string) {
return Utf8.parse(data).toString(Base64);
}
};
+18
View File
@@ -0,0 +1,18 @@
// Convert object's keys to camelCase format
import { camelCase, isPlainObject } from "lodash";
export function toCamelCase(obj: Record<string, any>): any {
if (Array.isArray(obj)) {
return obj.map(toCamelCase);
}
else if (isPlainObject(obj)) {
return Object.keys(obj).reduce((result, key) => {
const value = obj[key];
result[camelCase(key)] = typeof value === "object" ? toCamelCase(value) : value;
return result;
}, {} as any);
}
else {
return obj;
}
}
+5
View File
@@ -0,0 +1,5 @@
// Common utils (main/renderer)
export * from "./base64"
export * from "./camelCase"
export * from "./splitArray"
+19
View File
@@ -0,0 +1,19 @@
// Moved from dashboard/client/utils/arrays.ts
/**
* This function splits an array into two sub arrays on the first instance of
* element (from the left). If the array does not contain the element. The
* return value is defined to be `[array, [], false]`. If the element is in
* the array then the return value is `[left, right, true]` where `left` is
* the elements of `array` from `[0, index)` and `right` is `(index, length)`
* @param array the full array to split into two sub-arrays
* @param element the element in the middle of the array
* @returns the left and right sub-arrays which when conjoined with `element`
* is the same as `array`, and `true`
*/
export function splitArray<T>(array: T[], element: T): [T[], T[], boolean] {
const index = array.indexOf(element);
if (index < 0) {
return [array, [], false];
}
return [array.slice(0, index), array.slice(index + 1, array.length), true]
}
+31
View File
@@ -0,0 +1,31 @@
import { splitArray } from "./splitArray";
describe("split array on element tests", () => {
test("empty array", () => {
expect(splitArray([], 10)).toStrictEqual([[], [], false]);
});
test("one element, not in array", () => {
expect(splitArray([1], 10)).toStrictEqual([[1], [], false]);
});
test("ten elements, not in array", () => {
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 10)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [], false]);
});
test("one elements, in array", () => {
expect(splitArray([1], 1)).toStrictEqual([[], [], true]);
});
test("ten elements, in front array", () => {
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0)).toStrictEqual([[], [1, 2, 3, 4, 5, 6, 7, 8, 9], true]);
});
test("ten elements, in middle array", () => {
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 4)).toStrictEqual([[0, 1, 2, 3], [5, 6, 7, 8, 9], true]);
});
test("ten elements, in end array", () => {
expect(splitArray([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 9)).toStrictEqual([[0, 1, 2, 3, 4, 5, 6, 7, 8], [], true]);
});
});
+39
View File
@@ -0,0 +1,39 @@
// App's common configuration for any process (main, renderer, build pipeline, etc.)
import path from "path";
// Temp
export const reactAppName = "app_react"
export const vueAppName = "app_vue"
// Flags
export const isMac = process.platform === "darwin"
export const isWindows = process.platform === "win32"
export const isDebugging = process.env.DEBUG === "true";
export const isProduction = process.env.NODE_ENV === "production"
export const isDevelopment = isDebugging || !isProduction;
export const buildVersion = process.env.BUILD_VERSION;
export const isTestEnv = !!process.env.JEST_WORKER_ID;
// Paths
export const contextDir = process.cwd();
export const staticDir = path.join(contextDir, "static");
export const outDir = path.join(contextDir, "out");
export const mainDir = path.join(contextDir, "src/main");
export const rendererDir = path.join(contextDir, "src/renderer");
export const htmlTemplate = path.resolve(rendererDir, "template.html");
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
// Apis
export const staticProto = "static://"
export const apiPrefix = {
BASE: '/api',
TERMINAL: '/api-terminal', // terminal api
KUBE_BASE: '/api-kube', // kubernetes cluster api
KUBE_HELM: '/api-helm', // helm charts api
KUBE_RESOURCE_APPLIER: "/api-resource",
};
// Links
export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues"
export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI"
+1 -1
View File
@@ -1,4 +1,4 @@
import * as ElectronStore from "electron-store"
import ElectronStore from "electron-store"
import { clusterStore } from "./cluster-store"
export interface WorkspaceData {