diff --git a/.gitignore b/.gitignore index d018f3b251..11090d503a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ types/extension-renderer-api.d.ts extensions/*/dist docs/extensions/api site/ +logs/ diff --git a/Makefile b/Makefile index d2b7cc9dd8..29e8b1e041 100644 --- a/Makefile +++ b/Makefile @@ -53,18 +53,24 @@ integration-linux: binaries/client build-extension-types build-extensions # rm -rf ${HOME}/.config/Lens # endif yarn build:linux + mkdir -p logs + rm -r ./logs/* yarn integration .PHONY: integration-mac integration-mac: binaries/client build-extension-types build-extensions # rm ${HOME}/Library/Application\ Support/Lens yarn build:mac + mkdir -p logs + rm -r ./logs/* yarn integration .PHONY: integration-win integration-win: binaries/client build-extension-types build-extensions # rm %APPDATA%/Lens yarn build:win + mkdir -p logs + rm -r ./logs/* yarn integration .PHONY: build diff --git a/integration/__tests__/app.tests.ts b/integration/__tests__/app.tests.ts index 5f0fe9df04..5f83d5f7ad 100644 --- a/integration/__tests__/app.tests.ts +++ b/integration/__tests__/app.tests.ts @@ -13,7 +13,7 @@ import * as utils from "../helpers/utils"; import { listHelmRepositories } from "../helpers/utils"; import { fail } from "assert"; -jest.setTimeout(60000); +jest.setTimeout(60 * 1000 * 10); // 10m // FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below) describe("Lens integration tests", () => { diff --git a/integration/__tests__/cluster-pages.tests.ts b/integration/__tests__/cluster-pages.tests.ts index e2e9d14e2a..2cf80409fe 100644 --- a/integration/__tests__/cluster-pages.tests.ts +++ b/integration/__tests__/cluster-pages.tests.ts @@ -12,7 +12,7 @@ import * as util from "util"; export const promiseExec = util.promisify(exec); -jest.setTimeout(60000); +jest.setTimeout(60 * 1000 * 10); // 10m // FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below) describe("Lens cluster pages", () => { diff --git a/integration/__tests__/command-palette.tests.ts b/integration/__tests__/command-palette.tests.ts index 6f924f5524..a015b4e581 100644 --- a/integration/__tests__/command-palette.tests.ts +++ b/integration/__tests__/command-palette.tests.ts @@ -1,7 +1,7 @@ import { Application } from "spectron"; import * as utils from "../helpers/utils"; -jest.setTimeout(60000); +jest.setTimeout(60 * 1000 * 10); // 10m describe("Lens command palette", () => { let app: Application; diff --git a/integration/helpers/utils.ts b/integration/helpers/utils.ts index bb41e65bdd..0864782ee1 100644 --- a/integration/helpers/utils.ts +++ b/integration/helpers/utils.ts @@ -1,6 +1,7 @@ -import { Application } from "spectron"; +import { AppConstructorOptions, Application } from "spectron"; import * as util from "util"; import { exec } from "child_process"; +import { isCI } from "../../src/common/vars"; const AppPaths: Partial> = { "win32": "./dist/win-unpacked/OpenLens.exe", @@ -50,15 +51,24 @@ export function describeIf(condition: boolean) { } export function setup(): Application { - return new Application({ + const args: AppConstructorOptions = { path: AppPaths[process.platform], // path to electron app args: [], - startTimeout: 30000, - waitTimeout: 60000, + startTimeout: 60000, + waitTimeout: 30000, env: { - CICD: "true" - } - }); + CICD: "true", + }, + chromeDriverLogPath: "./logs/chromeDriver", + webdriverLogPath: "./logs/webdriver", + }; + + if (!isCI) { + args.chromeDriverLogPath = "./logs/chromeDriver"; + args.webdriverLogPath = "./logs/webdriver"; + } + + return new Application(args); } export const keys = { diff --git a/src/common/vars.ts b/src/common/vars.ts index 03d3f1c63d..c3bc68d720 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -13,6 +13,7 @@ export const isProduction = process.env.NODE_ENV === "production"; export const isTestEnv = !!process.env.JEST_WORKER_ID; export const isDevelopment = !isTestEnv && !isProduction; export const isPublishConfigured = Object.keys(packageInfo.build).includes("publish"); +export const isCI = Boolean(process.env.CI); export const productName = packageInfo.productName; export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`; diff --git a/src/renderer/components/+namespaces/add-namespace-dialog.tsx b/src/renderer/components/+namespaces/add-namespace-dialog.tsx index 1ad2efae59..fad1a21785 100644 --- a/src/renderer/components/+namespaces/add-namespace-dialog.tsx +++ b/src/renderer/components/+namespaces/add-namespace-dialog.tsx @@ -8,7 +8,7 @@ import { Wizard, WizardStep } from "../wizard"; import { namespaceStore } from "./namespace.store"; import { Namespace } from "../../api/endpoints"; import { Input } from "../input"; -import { systemName } from "../input/input_validators"; +import { namespaceValue } from "../input/input_validators"; import { Notifications } from "../notifications"; interface Props extends DialogProps { @@ -49,19 +49,15 @@ export class AddNamespaceDialog extends React.Component { }; render() { - const { ...dialogProps } = this.props; - const { namespace } = this; - const header =
Create Namespace
; - return ( - + Create Namespace} done={AddNamespaceDialog.close}> { required autoFocus iconLeft="layers" placeholder="Namespace" - validators={systemName} - value={namespace} onChange={v => this.namespace = v.toLowerCase()} + validators={namespaceValue} + value={this.namespace} + onChange={v => this.namespace = v.toLowerCase()} /> diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index 582c08b261..2bae45f143 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -47,9 +47,18 @@ const defaultProps: Partial = { export class Input extends React.Component { static defaultProps = defaultProps as object; - public inputRef = React.createRef(); - public validators: InputValidator[] = []; - public asyncValidators: AsyncInputValidator[] = []; + inputRef = React.createRef(); + validators = [ + ...conditionalValidators.filter(({ condition }) => condition(this.props)), + ...[this.props.validators], + ] + .flat() + .filter(Boolean); + asyncValidators = [ + this.props.asyncValidators + ] + .flat() + .filter(Boolean); @observable errors: React.ReactNode[] = []; @observable dirty = Boolean(this.props.showErrorInitially); @@ -62,17 +71,7 @@ export class Input extends React.Component { } componentDidMount() { - const { validators, asyncValidators, showErrorInitially } = this.props; - - this.validators = conditionalValidators - // add conditional validators if matches input props - .filter(validator => validator.condition(this.props)) - // add custom validators - .concat(validators); - - this.asyncValidators = [asyncValidators].flat(); - - if (showErrorInitially) { + if (this.props.showErrorInitially) { this.runValidatorsRaw(this.getValue()); } diff --git a/src/renderer/components/input/input_validators.ts b/src/renderer/components/input/input_validators.ts index f40607aee7..c78019a3d4 100644 --- a/src/renderer/components/input/input_validators.ts +++ b/src/renderer/components/input/input_validators.ts @@ -4,6 +4,10 @@ import fse from "fs-extra"; export type ValidatorMessage = ReactNode | ((value: string, props?: InputProps) => ReactNode | string); +export interface ConditionalInputValidator extends InputValidator { + condition(props: InputProps): boolean; // auto-bind condition depending on input props +} + export interface InputValidator { condition?(props: InputProps): boolean; // auto-bind condition depending on input props message: ValidatorMessage; @@ -16,19 +20,19 @@ export interface AsyncInputValidator { validate(value: string, props?: InputProps): Promise; } -export const isRequired: InputValidator = { +export const isRequired: ConditionalInputValidator = { condition: ({ required }) => required, message: "This field is required", validate: value => !!value.trim(), }; -export const isEmail: InputValidator = { +export const isEmail: ConditionalInputValidator = { condition: ({ type }) => type === "email", message: "Must be an email", validate: value => !!value.match(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/), }; -export const isNumber: InputValidator = { +export const isNumber: ConditionalInputValidator = { condition: ({ type }) => type === "number", message: "Must be a number", validate: (value, { min, max }) => { @@ -42,7 +46,7 @@ export const isNumber: InputValidator = { }, }; -export const isUrl: InputValidator = { +export const isUrl: ConditionalInputValidator = { condition: ({ type }) => type === "url", message: "Must be a valid URL", validate: value => { @@ -56,7 +60,7 @@ export const isUrl: InputValidator = { export const isExtensionNameInstallRegex = /^(?(@[-\w]+\/)?[-\w]+)(@(?\d\.\d\.\d(-\w+)?))?$/gi; -export const isExtensionNameInstall: InputValidator = { +export const isExtensionNameInstall: ConditionalInputValidator = { condition: ({ type }) => type === "text", message: "Not an extension name with optional version", validate: value => value.match(isExtensionNameInstallRegex) !== null, @@ -76,13 +80,13 @@ export const isPath: AsyncInputValidator = { }, }; -export const minLength: InputValidator = { +export const minLength: ConditionalInputValidator = { condition: ({ minLength }) => !!minLength, message: (value, { minLength }) => `Minimum length is ${minLength}`, validate: (value, { minLength }) => value.length >= minLength, }; -export const maxLength: InputValidator = { +export const maxLength: ConditionalInputValidator = { condition: ({ maxLength }) => !!maxLength, message: (value, { maxLength }) => `Maximum length is ${maxLength}`, validate: (value, { maxLength }) => value.length <= maxLength, @@ -105,6 +109,6 @@ export const accountId: InputValidator = { validate: value => (isEmail.validate(value) || systemName.validate(value)) }; -export const conditionalValidators = [ +export const conditionalValidators: ConditionalInputValidator[] = [ isRequired, isEmail, isNumber, isUrl, minLength, maxLength ]; diff --git a/webpack.extensions.ts b/webpack.extensions.ts index ce35d537dd..857d6195cf 100644 --- a/webpack.extensions.ts +++ b/webpack.extensions.ts @@ -26,6 +26,7 @@ export default function generateExtensionTypes(): webpack.Configuration { optimization: { minimize: false, // speed up types compilation }, + stats: "errors-warnings", module: { rules: [ {