mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix Input not working when not provided with an AsyncValidator
- Added logging by default for non-CI environments running integration tests - Increasing the timeout on integration tests so that spectron is more likely to error out instead (should help with debugging tests) Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
c7148bb980
commit
5c840a8af5
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@ types/extension-renderer-api.d.ts
|
|||||||
extensions/*/dist
|
extensions/*/dist
|
||||||
docs/extensions/api
|
docs/extensions/api
|
||||||
site/
|
site/
|
||||||
|
logs/
|
||||||
|
|||||||
6
Makefile
6
Makefile
@ -53,18 +53,24 @@ integration-linux: binaries/client build-extension-types build-extensions
|
|||||||
# rm -rf ${HOME}/.config/Lens
|
# rm -rf ${HOME}/.config/Lens
|
||||||
# endif
|
# endif
|
||||||
yarn build:linux
|
yarn build:linux
|
||||||
|
mkdir -p logs
|
||||||
|
rm -r ./logs/*
|
||||||
yarn integration
|
yarn integration
|
||||||
|
|
||||||
.PHONY: integration-mac
|
.PHONY: integration-mac
|
||||||
integration-mac: binaries/client build-extension-types build-extensions
|
integration-mac: binaries/client build-extension-types build-extensions
|
||||||
# rm ${HOME}/Library/Application\ Support/Lens
|
# rm ${HOME}/Library/Application\ Support/Lens
|
||||||
yarn build:mac
|
yarn build:mac
|
||||||
|
mkdir -p logs
|
||||||
|
rm -r ./logs/*
|
||||||
yarn integration
|
yarn integration
|
||||||
|
|
||||||
.PHONY: integration-win
|
.PHONY: integration-win
|
||||||
integration-win: binaries/client build-extension-types build-extensions
|
integration-win: binaries/client build-extension-types build-extensions
|
||||||
# rm %APPDATA%/Lens
|
# rm %APPDATA%/Lens
|
||||||
yarn build:win
|
yarn build:win
|
||||||
|
mkdir -p logs
|
||||||
|
rm -r ./logs/*
|
||||||
yarn integration
|
yarn integration
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import * as utils from "../helpers/utils";
|
|||||||
import { listHelmRepositories } from "../helpers/utils";
|
import { listHelmRepositories } from "../helpers/utils";
|
||||||
import { fail } from "assert";
|
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)
|
// FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below)
|
||||||
describe("Lens integration tests", () => {
|
describe("Lens integration tests", () => {
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import * as util from "util";
|
|||||||
|
|
||||||
export const promiseExec = util.promisify(exec);
|
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)
|
// FIXME (!): improve / simplify all css-selectors + use [data-test-id="some-id"] (already used in some tests below)
|
||||||
describe("Lens cluster pages", () => {
|
describe("Lens cluster pages", () => {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Application } from "spectron";
|
import { Application } from "spectron";
|
||||||
import * as utils from "../helpers/utils";
|
import * as utils from "../helpers/utils";
|
||||||
|
|
||||||
jest.setTimeout(60000);
|
jest.setTimeout(60 * 1000 * 10); // 10m
|
||||||
|
|
||||||
describe("Lens command palette", () => {
|
describe("Lens command palette", () => {
|
||||||
let app: Application;
|
let app: Application;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Application } from "spectron";
|
import { AppConstructorOptions, Application } from "spectron";
|
||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
|
import { isCI } from "../../src/common/vars";
|
||||||
|
|
||||||
const AppPaths: Partial<Record<NodeJS.Platform, string>> = {
|
const AppPaths: Partial<Record<NodeJS.Platform, string>> = {
|
||||||
"win32": "./dist/win-unpacked/OpenLens.exe",
|
"win32": "./dist/win-unpacked/OpenLens.exe",
|
||||||
@ -50,15 +51,24 @@ export function describeIf(condition: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setup(): Application {
|
export function setup(): Application {
|
||||||
return new Application({
|
const args: AppConstructorOptions = {
|
||||||
path: AppPaths[process.platform], // path to electron app
|
path: AppPaths[process.platform], // path to electron app
|
||||||
args: [],
|
args: [],
|
||||||
startTimeout: 30000,
|
startTimeout: 60000,
|
||||||
waitTimeout: 60000,
|
waitTimeout: 30000,
|
||||||
env: {
|
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 = {
|
export const keys = {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export const isProduction = process.env.NODE_ENV === "production";
|
|||||||
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
||||||
export const isDevelopment = !isTestEnv && !isProduction;
|
export const isDevelopment = !isTestEnv && !isProduction;
|
||||||
export const isPublishConfigured = Object.keys(packageInfo.build).includes("publish");
|
export const isPublishConfigured = Object.keys(packageInfo.build).includes("publish");
|
||||||
|
export const isCI = Boolean(process.env.CI);
|
||||||
|
|
||||||
export const productName = packageInfo.productName;
|
export const productName = packageInfo.productName;
|
||||||
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { Wizard, WizardStep } from "../wizard";
|
|||||||
import { namespaceStore } from "./namespace.store";
|
import { namespaceStore } from "./namespace.store";
|
||||||
import { Namespace } from "../../api/endpoints";
|
import { Namespace } from "../../api/endpoints";
|
||||||
import { Input } from "../input";
|
import { Input } from "../input";
|
||||||
import { systemName } from "../input/input_validators";
|
import { namespaceValue } from "../input/input_validators";
|
||||||
import { Notifications } from "../notifications";
|
import { Notifications } from "../notifications";
|
||||||
|
|
||||||
interface Props extends DialogProps {
|
interface Props extends DialogProps {
|
||||||
@ -49,19 +49,15 @@ export class AddNamespaceDialog extends React.Component<Props> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { ...dialogProps } = this.props;
|
|
||||||
const { namespace } = this;
|
|
||||||
const header = <h5>Create Namespace</h5>;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
{...dialogProps}
|
{...this.props}
|
||||||
className="AddNamespaceDialog"
|
className="AddNamespaceDialog"
|
||||||
isOpen={AddNamespaceDialog.isOpen}
|
isOpen={AddNamespaceDialog.isOpen}
|
||||||
onOpen={this.reset}
|
onOpen={this.reset}
|
||||||
close={AddNamespaceDialog.close}
|
close={AddNamespaceDialog.close}
|
||||||
>
|
>
|
||||||
<Wizard header={header} done={AddNamespaceDialog.close}>
|
<Wizard header={<h5>Create Namespace</h5>} done={AddNamespaceDialog.close}>
|
||||||
<WizardStep
|
<WizardStep
|
||||||
contentClass="flex gaps column"
|
contentClass="flex gaps column"
|
||||||
nextLabel="Create"
|
nextLabel="Create"
|
||||||
@ -71,8 +67,9 @@ export class AddNamespaceDialog extends React.Component<Props> {
|
|||||||
required autoFocus
|
required autoFocus
|
||||||
iconLeft="layers"
|
iconLeft="layers"
|
||||||
placeholder="Namespace"
|
placeholder="Namespace"
|
||||||
validators={systemName}
|
validators={namespaceValue}
|
||||||
value={namespace} onChange={v => this.namespace = v.toLowerCase()}
|
value={this.namespace}
|
||||||
|
onChange={v => this.namespace = v.toLowerCase()}
|
||||||
/>
|
/>
|
||||||
</WizardStep>
|
</WizardStep>
|
||||||
</Wizard>
|
</Wizard>
|
||||||
|
|||||||
@ -47,9 +47,18 @@ const defaultProps: Partial<InputProps> = {
|
|||||||
export class Input extends React.Component<InputProps> {
|
export class Input extends React.Component<InputProps> {
|
||||||
static defaultProps = defaultProps as object;
|
static defaultProps = defaultProps as object;
|
||||||
|
|
||||||
public inputRef = React.createRef<InputElement>();
|
inputRef = React.createRef<InputElement>();
|
||||||
public validators: InputValidator[] = [];
|
validators = [
|
||||||
public asyncValidators: AsyncInputValidator[] = [];
|
...conditionalValidators.filter(({ condition }) => condition(this.props)),
|
||||||
|
...[this.props.validators],
|
||||||
|
]
|
||||||
|
.flat()
|
||||||
|
.filter(Boolean);
|
||||||
|
asyncValidators = [
|
||||||
|
this.props.asyncValidators
|
||||||
|
]
|
||||||
|
.flat()
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
@observable errors: React.ReactNode[] = [];
|
@observable errors: React.ReactNode[] = [];
|
||||||
@observable dirty = Boolean(this.props.showErrorInitially);
|
@observable dirty = Boolean(this.props.showErrorInitially);
|
||||||
@ -62,17 +71,7 @@ export class Input extends React.Component<InputProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { validators, asyncValidators, showErrorInitially } = this.props;
|
if (this.props.showErrorInitially) {
|
||||||
|
|
||||||
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) {
|
|
||||||
this.runValidatorsRaw(this.getValue());
|
this.runValidatorsRaw(this.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,10 @@ import fse from "fs-extra";
|
|||||||
|
|
||||||
export type ValidatorMessage = ReactNode | ((value: string, props?: InputProps) => ReactNode | string);
|
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 {
|
export interface InputValidator {
|
||||||
condition?(props: InputProps): boolean; // auto-bind condition depending on input props
|
condition?(props: InputProps): boolean; // auto-bind condition depending on input props
|
||||||
message: ValidatorMessage;
|
message: ValidatorMessage;
|
||||||
@ -16,19 +20,19 @@ export interface AsyncInputValidator {
|
|||||||
validate(value: string, props?: InputProps): Promise<boolean>;
|
validate(value: string, props?: InputProps): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isRequired: InputValidator = {
|
export const isRequired: ConditionalInputValidator = {
|
||||||
condition: ({ required }) => required,
|
condition: ({ required }) => required,
|
||||||
message: "This field is required",
|
message: "This field is required",
|
||||||
validate: value => !!value.trim(),
|
validate: value => !!value.trim(),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isEmail: InputValidator = {
|
export const isEmail: ConditionalInputValidator = {
|
||||||
condition: ({ type }) => type === "email",
|
condition: ({ type }) => type === "email",
|
||||||
message: "Must be an 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,}))$/),
|
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",
|
condition: ({ type }) => type === "number",
|
||||||
message: "Must be a number",
|
message: "Must be a number",
|
||||||
validate: (value, { min, max }) => {
|
validate: (value, { min, max }) => {
|
||||||
@ -42,7 +46,7 @@ export const isNumber: InputValidator = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isUrl: InputValidator = {
|
export const isUrl: ConditionalInputValidator = {
|
||||||
condition: ({ type }) => type === "url",
|
condition: ({ type }) => type === "url",
|
||||||
message: "Must be a valid URL",
|
message: "Must be a valid URL",
|
||||||
validate: value => {
|
validate: value => {
|
||||||
@ -56,7 +60,7 @@ export const isUrl: InputValidator = {
|
|||||||
|
|
||||||
export const isExtensionNameInstallRegex = /^(?<name>(@[-\w]+\/)?[-\w]+)(@(?<version>\d\.\d\.\d(-\w+)?))?$/gi;
|
export const isExtensionNameInstallRegex = /^(?<name>(@[-\w]+\/)?[-\w]+)(@(?<version>\d\.\d\.\d(-\w+)?))?$/gi;
|
||||||
|
|
||||||
export const isExtensionNameInstall: InputValidator = {
|
export const isExtensionNameInstall: ConditionalInputValidator = {
|
||||||
condition: ({ type }) => type === "text",
|
condition: ({ type }) => type === "text",
|
||||||
message: "Not an extension name with optional version",
|
message: "Not an extension name with optional version",
|
||||||
validate: value => value.match(isExtensionNameInstallRegex) !== null,
|
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,
|
condition: ({ minLength }) => !!minLength,
|
||||||
message: (value, { minLength }) => `Minimum length is ${minLength}`,
|
message: (value, { minLength }) => `Minimum length is ${minLength}`,
|
||||||
validate: (value, { minLength }) => value.length >= minLength,
|
validate: (value, { minLength }) => value.length >= minLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const maxLength: InputValidator = {
|
export const maxLength: ConditionalInputValidator = {
|
||||||
condition: ({ maxLength }) => !!maxLength,
|
condition: ({ maxLength }) => !!maxLength,
|
||||||
message: (value, { maxLength }) => `Maximum length is ${maxLength}`,
|
message: (value, { maxLength }) => `Maximum length is ${maxLength}`,
|
||||||
validate: (value, { maxLength }) => value.length <= maxLength,
|
validate: (value, { maxLength }) => value.length <= maxLength,
|
||||||
@ -105,6 +109,6 @@ export const accountId: InputValidator = {
|
|||||||
validate: value => (isEmail.validate(value) || systemName.validate(value))
|
validate: value => (isEmail.validate(value) || systemName.validate(value))
|
||||||
};
|
};
|
||||||
|
|
||||||
export const conditionalValidators = [
|
export const conditionalValidators: ConditionalInputValidator[] = [
|
||||||
isRequired, isEmail, isNumber, isUrl, minLength, maxLength
|
isRequired, isEmail, isNumber, isUrl, minLength, maxLength
|
||||||
];
|
];
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export default function generateExtensionTypes(): webpack.Configuration {
|
|||||||
optimization: {
|
optimization: {
|
||||||
minimize: false, // speed up types compilation
|
minimize: false, // speed up types compilation
|
||||||
},
|
},
|
||||||
|
stats: "errors-warnings",
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user