1
0
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:
Sebastian Malton 2021-05-07 16:06:47 -04:00
parent c7148bb980
commit 5c840a8af5
11 changed files with 60 additions and 41 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@ types/extension-renderer-api.d.ts
extensions/*/dist
docs/extensions/api
site/
logs/

View File

@ -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

View File

@ -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", () => {

View File

@ -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", () => {

View File

@ -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;

View File

@ -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<Record<NodeJS.Platform, string>> = {
"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 = {

View File

@ -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" : ""}`;

View File

@ -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<Props> {
};
render() {
const { ...dialogProps } = this.props;
const { namespace } = this;
const header = <h5>Create Namespace</h5>;
return (
<Dialog
{...dialogProps}
{...this.props}
className="AddNamespaceDialog"
isOpen={AddNamespaceDialog.isOpen}
onOpen={this.reset}
close={AddNamespaceDialog.close}
>
<Wizard header={header} done={AddNamespaceDialog.close}>
<Wizard header={<h5>Create Namespace</h5>} done={AddNamespaceDialog.close}>
<WizardStep
contentClass="flex gaps column"
nextLabel="Create"
@ -71,8 +67,9 @@ export class AddNamespaceDialog extends React.Component<Props> {
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()}
/>
</WizardStep>
</Wizard>

View File

@ -47,9 +47,18 @@ const defaultProps: Partial<InputProps> = {
export class Input extends React.Component<InputProps> {
static defaultProps = defaultProps as object;
public inputRef = React.createRef<InputElement>();
public validators: InputValidator[] = [];
public asyncValidators: AsyncInputValidator[] = [];
inputRef = React.createRef<InputElement>();
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<InputProps> {
}
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());
}

View File

@ -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<boolean>;
}
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 = /^(?<name>(@[-\w]+\/)?[-\w]+)(@(?<version>\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
];

View File

@ -26,6 +26,7 @@ export default function generateExtensionTypes(): webpack.Configuration {
optimization: {
minimize: false, // speed up types compilation
},
stats: "errors-warnings",
module: {
rules: [
{