Update react-scripts & fix eslint
This commit is contained in:
parent
14d15abd94
commit
43c4eba0fa
|
|
@ -39,22 +39,22 @@ rules:
|
|||
import/first: error
|
||||
import/no-unused-modules: error
|
||||
|
||||
unicorn/no-abusive-eslint-disable: error
|
||||
unicorn/no-abusive-eslint-disable: off
|
||||
unicorn/no-array-instanceof: error
|
||||
unicorn/no-unreadable-array-destructuring: error
|
||||
unicorn/no-zero-fractions: error
|
||||
|
||||
react/jsx-key: error
|
||||
react/jsx-pascal-case: error
|
||||
react/destructuring-assignment: warn
|
||||
react/function-component-definition: [error, {namedComponents: arrow-function, unnamedComponents: arrow-function}]
|
||||
react/destructuring-assignment: off
|
||||
react/function-component-definition: off
|
||||
react/no-array-index-key: error
|
||||
react/no-deprecated: error
|
||||
react/no-deprecated: off
|
||||
react/no-string-refs: error
|
||||
react/no-this-in-sfc: error
|
||||
react/no-typos: error
|
||||
react/no-unknown-property: error
|
||||
react/prefer-stateless-function: error
|
||||
react/prefer-stateless-function: off
|
||||
react/prop-types: off
|
||||
|
||||
jest/expect-expect: off
|
||||
|
|
@ -65,18 +65,21 @@ rules:
|
|||
"@typescript-eslint/await-thenable": error
|
||||
"@typescript-eslint/no-unused-vars": error
|
||||
"@typescript-eslint/no-use-before-define": off
|
||||
"@typescript-eslint/no-unsafe-call": off
|
||||
"@typescript-eslint/consistent-type-assertions": [error, {assertionStyle: as}]
|
||||
|
||||
"@typescript-eslint/no-extra-non-null-assertion": error
|
||||
"@typescript-eslint/no-inferrable-types": error
|
||||
"@typescript-eslint/no-this-alias": error
|
||||
"@typescript-eslint/no-throw-literal": error
|
||||
"@typescript-eslint/no-non-null-assertion": off
|
||||
"@typescript-eslint/prefer-nullish-coalescing": error
|
||||
"@typescript-eslint/prefer-optional-chain": error
|
||||
"@typescript-eslint/prefer-readonly": error
|
||||
"@typescript-eslint/prefer-readonly": off
|
||||
"@typescript-eslint/unbound-method": error
|
||||
"@typescript-eslint/no-empty-function": off
|
||||
"@typescript-eslint/explicit-module-boundary-types": off
|
||||
"@typescript-eslint/ban-ts-comment": off
|
||||
"@typescript-eslint/no-floating-promises": off
|
||||
"@typescript-eslint/no-unsafe-member-access": off
|
||||
"@typescript-eslint/no-unsafe-return": off
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=node",
|
||||
"eject": "react-scripts eject",
|
||||
"lint": "eslint \"src/*.{ts,tsx}\"",
|
||||
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
||||
"format": "prettier \"src/**/*.{ts,tsx}\" --write",
|
||||
"testformat": "prettier \"src/**/*.{ts,tsx}\" --list-different"
|
||||
},
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
"get-port": "^5.1.1",
|
||||
"prettier": "^2.1.1",
|
||||
"puppeteer": "^5.3.0",
|
||||
"react-scripts": "^3.4.3",
|
||||
"react-scripts": "^4.0.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"tree-kill": "^1.2.0",
|
||||
"typescript": "4.0.2",
|
||||
|
|
|
|||
|
|
@ -12,18 +12,16 @@ export class AppStore extends BaseStore<IApplication> {
|
|||
super();
|
||||
}
|
||||
|
||||
protected requestItems = (): Promise<IApplication[]> => {
|
||||
return axios
|
||||
protected requestItems = (): Promise<IApplication[]> =>
|
||||
axios
|
||||
.get<IApplication[]>(`${config.get('url')}application`)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
protected requestDelete = (id: number): Promise<void> => {
|
||||
return axios.delete(`${config.get('url')}application/${id}`).then(() => {
|
||||
protected requestDelete = (id: number): Promise<void> =>
|
||||
axios.delete(`${config.get('url')}application/${id}`).then(() => {
|
||||
this.onDelete();
|
||||
return this.snack('Application deleted');
|
||||
});
|
||||
};
|
||||
|
||||
@action
|
||||
public uploadImage = async (id: number, file: Blob): Promise<void> => {
|
||||
|
|
|
|||
|
|
@ -72,8 +72,7 @@ class Applications extends Component<Stores<'appStore'>> {
|
|||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{apps.map((app: IApplication) => {
|
||||
return (
|
||||
{apps.map((app: IApplication) => (
|
||||
<Row
|
||||
key={app.id}
|
||||
description={app.description}
|
||||
|
|
@ -85,8 +84,7 @@ class Applications extends Component<Stores<'appStore'>> {
|
|||
fEdit={() => (this.updateId = app.id)}
|
||||
noDelete={app.internal}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<input
|
||||
|
|
@ -133,7 +131,7 @@ class Applications extends Component<Stores<'appStore'>> {
|
|||
};
|
||||
|
||||
private onUploadImage = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,12 @@ interface IState {
|
|||
export default class UpdateDialog extends Component<IProps, IState> {
|
||||
public state = {name: '', description: ''};
|
||||
|
||||
public componentWillMount() {
|
||||
this.setState({name: this.props.initialName, description: this.props.initialDescription});
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: props.initialName,
|
||||
description: props.initialDescription,
|
||||
};
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ export class ClientStore extends BaseStore<IClient> {
|
|||
super();
|
||||
}
|
||||
|
||||
protected requestItems = (): Promise<IClient[]> => {
|
||||
return axios.get<IClient[]>(`${config.get('url')}client`).then((response) => response.data);
|
||||
};
|
||||
protected requestItems = (): Promise<IClient[]> =>
|
||||
axios.get<IClient[]>(`${config.get('url')}client`).then((response) => response.data);
|
||||
|
||||
protected requestDelete(id: number): Promise<void> {
|
||||
return axios
|
||||
|
|
|
|||
|
|
@ -64,8 +64,7 @@ class Clients extends Component<Stores<'clientStore'>> {
|
|||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{clients.map((client: IClient) => {
|
||||
return (
|
||||
{clients.map((client: IClient) => (
|
||||
<Row
|
||||
key={client.id}
|
||||
name={client.name}
|
||||
|
|
@ -73,8 +72,7 @@ class Clients extends Component<Stores<'clientStore'>> {
|
|||
fEdit={() => (this.updateId = client.id)}
|
||||
fDelete={() => (this.deleteId = client.id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -21,8 +21,11 @@ interface IState {
|
|||
export default class UpdateDialog extends Component<IProps, IState> {
|
||||
public state = {name: ''};
|
||||
|
||||
public componentWillMount() {
|
||||
this.setState({name: this.props.initialName});
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: props.initialName,
|
||||
};
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
|
|
|||
|
|
@ -38,9 +38,8 @@ export abstract class BaseStore<T extends HasID> implements IClearable {
|
|||
return item;
|
||||
};
|
||||
|
||||
public getByIDOrUndefined = (id: number): T | undefined => {
|
||||
return this.items.find((hasId: HasID) => hasId.id === id);
|
||||
};
|
||||
public getByIDOrUndefined = (id: number): T | undefined =>
|
||||
this.items.find((hasId: HasID) => hasId.id === id);
|
||||
|
||||
public getItems = (): T[] => this.items;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ interface ConnectionErrorBannerProps {
|
|||
message: string;
|
||||
}
|
||||
|
||||
export const ConnectionErrorBanner = ({height, retry, message}: ConnectionErrorBannerProps) => {
|
||||
return (
|
||||
export const ConnectionErrorBanner = ({height, retry, message}: ConnectionErrorBannerProps) => (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: '#e74c3c',
|
||||
|
|
@ -25,5 +24,4 @@ export const ConnectionErrorBanner = ({height, retry, message}: ConnectionErrorB
|
|||
</Button>
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9,15 +9,13 @@ const styles = () => ({
|
|||
});
|
||||
|
||||
interface IProps extends WithStyles<'paper'> {
|
||||
style?: object;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Container: React.SFC<IProps> = ({classes, children, style}) => {
|
||||
return (
|
||||
const Container: React.FC<IProps> = ({classes, children, style}) => (
|
||||
<Paper elevation={6} className={classes.paper} style={style}>
|
||||
{children}
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
export default withStyles(styles)(Container);
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import IconButton from '@material-ui/core/IconButton';
|
|||
import Typography from '@material-ui/core/Typography';
|
||||
import Visibility from '@material-ui/icons/Visibility';
|
||||
import VisibilityOff from '@material-ui/icons/VisibilityOff';
|
||||
import React, {Component} from 'react';
|
||||
import React, {Component, CSSProperties} from 'react';
|
||||
|
||||
interface IProps {
|
||||
value: string;
|
||||
style?: object;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ const ResponsiveButton: React.FC<{
|
|||
id?: string;
|
||||
onClick?: () => void;
|
||||
icon: React.ReactNode;
|
||||
}> = ({width, icon, children, label, ...rest}) => {
|
||||
}> = ({width, icon, label, ...rest}) => {
|
||||
if (width === 'xs' || width === 'sm') {
|
||||
return <IconButton {...rest}>{icon}</IconButton>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,9 +50,8 @@ const themeMap: Record<ThemeKey, Theme> = {
|
|||
}),
|
||||
};
|
||||
|
||||
const isThemeKey = (value: string | null): value is ThemeKey => {
|
||||
return value === 'light' || value === 'dark';
|
||||
};
|
||||
const isThemeKey = (value: string | null): value is ThemeKey =>
|
||||
value === 'light' || value === 'dark';
|
||||
|
||||
@observer
|
||||
class Layout extends React.Component<
|
||||
|
|
|
|||
|
|
@ -52,8 +52,7 @@ class Navigation extends Component<
|
|||
const userApps =
|
||||
apps.length === 0
|
||||
? null
|
||||
: apps.map((app) => {
|
||||
return (
|
||||
: apps.map((app) => (
|
||||
<Link
|
||||
onClick={() => setNavOpen(false)}
|
||||
className={`${classes.link} item`}
|
||||
|
|
@ -63,8 +62,7 @@ class Navigation extends Component<
|
|||
<ListItemText primary={app.name} />
|
||||
</ListItem>
|
||||
</Link>
|
||||
);
|
||||
});
|
||||
));
|
||||
|
||||
const placeholderItems = [
|
||||
<ListItem button disabled key={-1}>
|
||||
|
|
@ -108,8 +106,7 @@ class Navigation extends Component<
|
|||
|
||||
const ResponsiveDrawer: React.FC<
|
||||
DrawerProps & {navOpen: boolean; setNavOpen: (open: boolean) => void}
|
||||
> = ({navOpen, setNavOpen, children, ...rest}) => {
|
||||
return (
|
||||
> = ({navOpen, setNavOpen, children, ...rest}) => (
|
||||
<>
|
||||
<Hidden smUp implementation="css">
|
||||
<Drawer variant="temporary" open={navOpen} {...rest}>
|
||||
|
|
@ -125,7 +122,6 @@ const ResponsiveDrawer: React.FC<
|
|||
</Drawer>
|
||||
</Hidden>
|
||||
</>
|
||||
);
|
||||
};
|
||||
);
|
||||
|
||||
export default withStyles(styles, {withTheme: true})(inject('appStore')(Navigation));
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {observable} from 'mobx';
|
|||
import ReactInfinite from 'react-infinite';
|
||||
import {IMessage} from '../types';
|
||||
|
||||
interface IProps extends RouteComponentProps<{id: string}> {}
|
||||
type IProps = RouteComponentProps<{id: string}>;
|
||||
|
||||
interface IState {
|
||||
appId: number;
|
||||
|
|
@ -121,8 +121,7 @@ class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>,
|
|||
private deleteMessage = (message: IMessage) => () =>
|
||||
this.props.messagesStore.removeSingle(message);
|
||||
|
||||
private renderMessage = (message: IMessage) => {
|
||||
return (
|
||||
private renderMessage = (message: IMessage) => (
|
||||
<Message
|
||||
key={message.id}
|
||||
height={(height: number) => {
|
||||
|
|
@ -138,7 +137,6 @@ class Messages extends Component<IProps & Stores<'messagesStore' | 'appStore'>,
|
|||
extras={message.extras}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
private checkIfLoadMore() {
|
||||
const {appId} = this.state;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export class MessagesStore {
|
|||
);
|
||||
|
||||
state.messages.replace([...state.messages, ...pagedResult.messages]);
|
||||
state.nextSince = pagedResult.paging.since || 0;
|
||||
state.nextSince = pagedResult.paging.since ?? 0;
|
||||
state.hasMore = 'next' in pagedResult.paging;
|
||||
state.loaded = true;
|
||||
this.loading = false;
|
||||
|
|
@ -139,12 +139,10 @@ export class MessagesStore {
|
|||
.getItems()
|
||||
.reduce((all, app) => ({...all, [app.id]: app.image}), {});
|
||||
|
||||
return this.stateOf(appId, false).messages.map((message: IMessage) => {
|
||||
return {
|
||||
return this.stateOf(appId, false).messages.map((message: IMessage) => ({
|
||||
...message,
|
||||
image: appToImage[message.appid] || null,
|
||||
};
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
public get = createTransformer(this.getUnCached);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export class WebSocketStore {
|
|||
setTimeout(() => this.listen(callback), 30000);
|
||||
})
|
||||
.catch((error: AxiosError) => {
|
||||
if (error && error.response && error.response.status === 401) {
|
||||
if (error?.response?.status === 401) {
|
||||
this.snack('Could not authenticate with client token, logging out.');
|
||||
}
|
||||
});
|
||||
|
|
@ -47,5 +47,5 @@ export class WebSocketStore {
|
|||
this.ws = ws;
|
||||
};
|
||||
|
||||
public close = () => this.ws && this.ws.close(1000, 'WebSocketStore#close');
|
||||
public close = () => this.ws?.close(1000, 'WebSocketStore#close');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import Container from '../common/Container';
|
|||
import {inject, Stores} from '../inject';
|
||||
import {IPlugin} from '../types';
|
||||
|
||||
interface IProps extends RouteComponentProps<{id: string}> {}
|
||||
type IProps = RouteComponentProps<{id: string}>;
|
||||
|
||||
interface IState {
|
||||
displayText: string | null;
|
||||
|
|
@ -122,7 +122,7 @@ interface IPanelWrapperProps {
|
|||
icon?: React.ComponentType;
|
||||
}
|
||||
|
||||
const PanelWrapper: React.SFC<IPanelWrapperProps> = ({
|
||||
const PanelWrapper: React.FC<IPanelWrapperProps> = ({
|
||||
name,
|
||||
description,
|
||||
refresh,
|
||||
|
|
@ -175,7 +175,7 @@ class ConfigurerPanel extends Component<IConfigurerPanelProps, {unsavedChanges:
|
|||
theme: 'material',
|
||||
lineNumbers: true,
|
||||
}}
|
||||
onChange={(instance, data, value) => {
|
||||
onChange={(_, _1, value) => {
|
||||
let newConf: string | null = value;
|
||||
if (value === this.props.initialConfig) {
|
||||
newConf = null;
|
||||
|
|
@ -195,6 +195,7 @@ class ConfigurerPanel extends Component<IConfigurerPanelProps, {unsavedChanges:
|
|||
className="config-save"
|
||||
onClick={() => {
|
||||
const newConfig = this.state.unsavedChanges;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.props.save(newConfig!).then(() => {
|
||||
this.setState({unsavedChanges: null});
|
||||
});
|
||||
|
|
@ -210,7 +211,7 @@ interface IDisplayerPanelProps {
|
|||
pluginInfo: IPlugin;
|
||||
displayText: string;
|
||||
}
|
||||
const DisplayerPanel: React.SFC<IDisplayerPanelProps> = ({pluginInfo, displayText}) => (
|
||||
const DisplayerPanel: React.FC<IDisplayerPanelProps> = ({displayText}) => (
|
||||
<Typography variant="body2">
|
||||
<ReactMarkDown source={displayText} />
|
||||
</Typography>
|
||||
|
|
|
|||
|
|
@ -12,23 +12,16 @@ export class PluginStore extends BaseStore<IPlugin> {
|
|||
super();
|
||||
}
|
||||
|
||||
public requestConfig = (id: number): Promise<string> => {
|
||||
return axios
|
||||
.get(`${config.get('url')}plugin/${id}/config`)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
public requestConfig = (id: number): Promise<string> =>
|
||||
axios.get(`${config.get('url')}plugin/${id}/config`).then((response) => response.data);
|
||||
|
||||
public requestDisplay = (id: number): Promise<string> => {
|
||||
return axios
|
||||
.get(`${config.get('url')}plugin/${id}/display`)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
public requestDisplay = (id: number): Promise<string> =>
|
||||
axios.get(`${config.get('url')}plugin/${id}/display`).then((response) => response.data);
|
||||
|
||||
protected requestItems = (): Promise<IPlugin[]> => {
|
||||
return axios.get<IPlugin[]>(`${config.get('url')}plugin`).then((response) => response.data);
|
||||
};
|
||||
protected requestItems = (): Promise<IPlugin[]> =>
|
||||
axios.get<IPlugin[]>(`${config.get('url')}plugin`).then((response) => response.data);
|
||||
|
||||
protected requestDelete = (id: number): Promise<void> => {
|
||||
protected requestDelete = (): Promise<void> => {
|
||||
this.snack('Cannot delete plugin');
|
||||
throw new Error('Cannot delete plugin');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ class Plugins extends Component<Stores<'pluginStore'>> {
|
|||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{plugins.map((plugin: IPlugin) => {
|
||||
return (
|
||||
{plugins.map((plugin: IPlugin) => (
|
||||
<Row
|
||||
key={plugin.token}
|
||||
id={plugin.id}
|
||||
|
|
@ -54,8 +53,7 @@ class Plugins extends Component<Stores<'pluginStore'>> {
|
|||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -26,19 +26,20 @@ const hiddenToken = '•••••••••••••••';
|
|||
const $table = selector.table('#app-table');
|
||||
const $dialog = selector.form('#app-dialog');
|
||||
|
||||
const hasApp = (name: string, description: string, row: number): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
const hasApp = (
|
||||
name: string,
|
||||
description: string,
|
||||
row: number
|
||||
): (() => Promise<void>) => async () => {
|
||||
expect(await innerText(page, $table.cell(row, Col.Name))).toBe(name);
|
||||
expect(await innerText(page, $table.cell(row, Col.Token))).toBe(hiddenToken);
|
||||
expect(await innerText(page, $table.cell(row, Col.Description))).toBe(description);
|
||||
};
|
||||
};
|
||||
|
||||
export const updateApp = (
|
||||
const updateApp = (
|
||||
id: number,
|
||||
data: {name?: string; description?: string}
|
||||
): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
): (() => Promise<void>) => async () => {
|
||||
await page.click($table.cell(id, Col.EditUpdate, '.edit'));
|
||||
await page.waitForSelector($dialog.selector());
|
||||
if (data.name) {
|
||||
|
|
@ -53,17 +54,14 @@ export const updateApp = (
|
|||
}
|
||||
await page.click($dialog.button('.update'));
|
||||
await waitToDisappear(page, $dialog.selector());
|
||||
};
|
||||
};
|
||||
|
||||
export const createApp = (name: string, description: string): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
const createApp = (name: string, description: string): (() => Promise<void>) => async () => {
|
||||
await page.click('#create-app');
|
||||
await page.waitForSelector($dialog.selector());
|
||||
await page.type($dialog.input('.name'), name);
|
||||
await page.type($dialog.textarea('.description'), description);
|
||||
await page.click($dialog.button('.create'));
|
||||
};
|
||||
};
|
||||
|
||||
describe('Application', () => {
|
||||
|
|
|
|||
|
|
@ -21,14 +21,11 @@ enum Col {
|
|||
Delete = 4,
|
||||
}
|
||||
|
||||
const hasClient = (name: string, row: number): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
const hasClient = (name: string, row: number): (() => Promise<void>) => async () => {
|
||||
expect(await innerText(page, $table.cell(row, Col.Name))).toBe(name);
|
||||
};
|
||||
};
|
||||
|
||||
export const updateClient = (id: number, data: {name?: string}): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
const updateClient = (id: number, data: {name?: string}): (() => Promise<void>) => async () => {
|
||||
await page.click($table.cell(id, Col.Edit, '.edit'));
|
||||
await page.waitForSelector($dialog.selector());
|
||||
if (data.name) {
|
||||
|
|
@ -38,7 +35,6 @@ export const updateClient = (id: number, data: {name?: string}): (() => Promise<
|
|||
}
|
||||
await page.click($dialog.button('.update'));
|
||||
await waitToDisappear(page, $dialog.selector());
|
||||
};
|
||||
};
|
||||
|
||||
const $table = selector.table('#client-table');
|
||||
|
|
@ -57,14 +53,12 @@ describe('Client', () => {
|
|||
expect(await count(page, $table.rows())).toBe(1);
|
||||
});
|
||||
describe('create clients', () => {
|
||||
const createClient = (name: string): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
const createClient = (name: string): (() => Promise<void>) => async () => {
|
||||
await page.click('#create-client');
|
||||
await page.waitForSelector($dialog.selector());
|
||||
await page.type($dialog.input('.name'), name);
|
||||
await page.click($dialog.button('.create'));
|
||||
};
|
||||
};
|
||||
it('phone', createClient('phone'));
|
||||
it('desktop app', createClient('desktop app'));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -44,11 +44,10 @@ describe('Messages', () => {
|
|||
it('has url', async () => {
|
||||
expect(page.url()).toContain('/');
|
||||
});
|
||||
const createApp = (name: string) => {
|
||||
return axios
|
||||
const createApp = (name: string) =>
|
||||
axios
|
||||
.post<IApplication>(`${gotify.url}/application`, {name}, axiosAuth)
|
||||
.then((resp) => resp.data.token);
|
||||
};
|
||||
it('shows navigation', async () => {
|
||||
await page.waitForSelector(naviId);
|
||||
});
|
||||
|
|
@ -121,11 +120,10 @@ describe('Messages', () => {
|
|||
const backup2 = m('Backup done', 'Windows Server Backup finished (6.2GB).');
|
||||
const backup3 = m('Backup done', 'Gotify Backup finished (0.1MB).');
|
||||
|
||||
const createMessage = (msg: Partial<IMessage>, token: string) => {
|
||||
return axios.post<IMessage>(`${gotify.url}/message`, msg, {
|
||||
const createMessage = (msg: Partial<IMessage>, token: string) =>
|
||||
axios.post<IMessage>(`${gotify.url}/message`, msg, {
|
||||
headers: {'X-Gotify-Key': token},
|
||||
});
|
||||
};
|
||||
|
||||
const expectMessages = async (toCheck: {
|
||||
all: Msg[];
|
||||
|
|
|
|||
|
|
@ -49,13 +49,10 @@ const toggleEnabled = async (id: number) => {
|
|||
);
|
||||
};
|
||||
|
||||
const pluginInfo = async (className: string) => {
|
||||
return await innerText(page, `.plugin-info .${className} > span`);
|
||||
};
|
||||
const pluginInfo = async (className: string) =>
|
||||
await innerText(page, `.plugin-info .${className} > span`);
|
||||
|
||||
const getDisplayer = async () => {
|
||||
return await innerText(page, '.displayer');
|
||||
};
|
||||
const getDisplayer = async () => await innerText(page, '.displayer');
|
||||
|
||||
const hasReceivedMessage = async (title: RegExp, content: RegExp) => {
|
||||
await page.click('#message-navigation a');
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
export const heading = () => {
|
||||
return `main h4`;
|
||||
};
|
||||
export const heading = () => `main h4`;
|
||||
|
||||
export const table = (tableSelector: string) => {
|
||||
return {
|
||||
export const table = (tableSelector: string) => ({
|
||||
selector: () => tableSelector,
|
||||
rows: () => `${tableSelector} tbody tr`,
|
||||
row: (index: number) => `${tableSelector} tbody tr:nth-child(${index})`,
|
||||
cell: (index: number, col: number, suffix = '') =>
|
||||
`${tableSelector} tbody tr:nth-child(${index}) td:nth-child(${col}) ${suffix}`,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
export const form = (dialogSelector: string) => {
|
||||
return {
|
||||
export const form = (dialogSelector: string) => ({
|
||||
selector: () => dialogSelector,
|
||||
input: (selector: string) => `${dialogSelector} ${selector} input`,
|
||||
textarea: (selector: string) => `${dialogSelector} ${selector} textarea`,
|
||||
button: (selector: string) => `${dialogSelector} button${selector}`,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
export const $confirmDialog = form('.confirm-dialog');
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ const testFilePath = (): string => {
|
|||
return path.join(testBuildPath, filename);
|
||||
};
|
||||
|
||||
const waitForGotify = (url: string): Promise<void> => {
|
||||
return new Promise((resolve, err) => {
|
||||
const waitForGotify = (url: string): Promise<void> =>
|
||||
new Promise((resolve, err) => {
|
||||
wait({resources: [url], timeout: 40000}, (error: string) => {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
|
|
@ -93,7 +93,6 @@ const waitForGotify = (url: string): Promise<void> => {
|
|||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const buildGoPlugin = (filename: string, pluginPath: string): Promise<void> => {
|
||||
process.stdout.write(`### Building Plugin ${pluginPath}\n`);
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ describe('User', () => {
|
|||
name: string,
|
||||
password: string,
|
||||
isAdmin: boolean
|
||||
): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
): (() => Promise<void>) => async () => {
|
||||
await page.click('#create-user');
|
||||
await page.waitForSelector($dialog.selector());
|
||||
await page.type($dialog.input('.name'), name);
|
||||
|
|
@ -51,17 +50,18 @@ describe('User', () => {
|
|||
await page.click($dialog.button('.save-create'));
|
||||
await waitToDisappear(page, $dialog.selector());
|
||||
};
|
||||
};
|
||||
it('nicories', createUser('nicories', '123', false));
|
||||
it('jmattheis', createUser('jmattheis', 'noice', true));
|
||||
it('dude', createUser('dude', '1', false));
|
||||
});
|
||||
const hasUser = (name: string, isAdmin: boolean, row: number): (() => Promise<void>) => {
|
||||
return async () => {
|
||||
const hasUser = (
|
||||
name: string,
|
||||
isAdmin: boolean,
|
||||
row: number
|
||||
): (() => Promise<void>) => async () => {
|
||||
expect(await innerText(page, $table.cell(row, Col.Name))).toBe(name);
|
||||
expect(await innerText(page, $table.cell(row, Col.Admin))).toBe(isAdmin ? 'Yes' : 'No');
|
||||
};
|
||||
};
|
||||
|
||||
describe('has created users', () => {
|
||||
it('has four users', async () => {
|
||||
|
|
@ -131,10 +131,10 @@ describe('User', () => {
|
|||
it('does logout', async () => await auth.logout(page));
|
||||
it('can login with new password (admin)', async () =>
|
||||
await auth.login(page, 'admin', 'changed'));
|
||||
it('does logout', async () => await auth.logout(page));
|
||||
it('does logout admin', async () => await auth.logout(page));
|
||||
|
||||
it('can login with nicolas', async () => await auth.login(page, 'nicolas', '123'));
|
||||
it('does logout', async () => await auth.logout(page));
|
||||
it('does logout nicolas', async () => await auth.logout(page));
|
||||
it('can login with jmattheis', async () => await auth.login(page, 'jmattheis', 'unicorn'));
|
||||
it('does logout', async () => await auth.logout(page));
|
||||
it('does logout jmattheis', async () => await auth.logout(page));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ export const innerText = async (page: ElementHandle | Page, selector: string): P
|
|||
const element = await page.$(selector);
|
||||
const handle = await element!.getProperty('innerText');
|
||||
const value = await handle.jsonValue();
|
||||
return (value as object).toString().trim();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (value as any).toString().trim();
|
||||
};
|
||||
|
||||
export const clickByText = async (page: Page, selector: string, text: string): Promise<void> => {
|
||||
|
|
@ -21,42 +22,32 @@ export const clickByText = async (page: Page, selector: string, text: string): P
|
|||
);
|
||||
};
|
||||
|
||||
export const count = async (page: Page, selector: string): Promise<number> => {
|
||||
return page.$$(selector).then((elements) => elements.length);
|
||||
};
|
||||
export const count = async (page: Page, selector: string): Promise<number> =>
|
||||
page.$$(selector).then((elements) => elements.length);
|
||||
|
||||
export const waitToDisappear = async (page: Page, selector: string): Promise<JSHandle> => {
|
||||
return page.waitForFunction(
|
||||
(_selector: string) => !document.querySelector(_selector),
|
||||
{},
|
||||
selector
|
||||
);
|
||||
};
|
||||
export const waitToDisappear = async (page: Page, selector: string): Promise<JSHandle> =>
|
||||
page.waitForFunction((_selector: string) => !document.querySelector(_selector), {}, selector);
|
||||
|
||||
export const waitForCount = async (
|
||||
page: Page,
|
||||
selector: string,
|
||||
amount: number
|
||||
): Promise<JSHandle> => {
|
||||
return page.waitForFunction(
|
||||
): Promise<JSHandle> =>
|
||||
page.waitForFunction(
|
||||
(_selector: string, _amount: number) =>
|
||||
document.querySelectorAll(_selector).length === _amount,
|
||||
{},
|
||||
selector,
|
||||
amount
|
||||
);
|
||||
};
|
||||
|
||||
export const waitForExists = async (page: Page, selector: string, text: string): Promise<void> => {
|
||||
text = text.toLowerCase();
|
||||
await page.waitForFunction(
|
||||
(_selector: string, _text: string) => {
|
||||
return (
|
||||
(_selector: string, _text: string) =>
|
||||
Array.from(document.querySelectorAll(_selector)).filter(
|
||||
(element) => element.textContent!.toLowerCase().trim() === _text
|
||||
).length > 0
|
||||
);
|
||||
},
|
||||
).length > 0,
|
||||
{},
|
||||
selector,
|
||||
text
|
||||
|
|
@ -67,7 +58,6 @@ export const clearField = async (element: ElementHandle | Page, selector: string
|
|||
const elementHandle = await element.$(selector);
|
||||
if (!elementHandle) {
|
||||
fail();
|
||||
return;
|
||||
}
|
||||
await elementHandle.click();
|
||||
await elementHandle.focus();
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@ declare module 'react-timeago' {
|
|||
date: string;
|
||||
}
|
||||
|
||||
export default class TimeAgo extends React.Component<ITimeAgoProps, any> {}
|
||||
export default class TimeAgo extends React.Component<ITimeAgoProps, unknown> {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ interface IState {
|
|||
|
||||
export default class AddEditDialog extends Component<IProps, IState> {
|
||||
public state = {
|
||||
name: '',
|
||||
name: this.props.name ?? '',
|
||||
pass: '',
|
||||
admin: false,
|
||||
admin: this.props.admin ?? false,
|
||||
};
|
||||
|
||||
public render() {
|
||||
|
|
@ -107,11 +107,6 @@ export default class AddEditDialog extends Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
public componentWillMount() {
|
||||
const {name, admin} = this.props;
|
||||
this.setState({...this.state, name: name || '', admin: admin || false});
|
||||
}
|
||||
|
||||
private handleChange(propertyName: string, event: ChangeEvent<HTMLInputElement>) {
|
||||
const state = this.state;
|
||||
state[propertyName] = event.target.value;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,8 @@ export class UserStore extends BaseStore<IUser> {
|
|||
super();
|
||||
}
|
||||
|
||||
protected requestItems = (): Promise<IUser[]> => {
|
||||
return axios.get<IUser[]>(`${config.get('url')}user`).then((response) => response.data);
|
||||
};
|
||||
protected requestItems = (): Promise<IUser[]> =>
|
||||
axios.get<IUser[]>(`${config.get('url')}user`).then((response) => response.data);
|
||||
|
||||
protected requestDelete(id: number): Promise<void> {
|
||||
return axios
|
||||
|
|
|
|||
|
|
@ -90,8 +90,7 @@ class Users extends Component<WithStyles<'wrapper'> & Stores<'userStore'>> {
|
|||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{users.map((user: IUser) => {
|
||||
return (
|
||||
{users.map((user: IUser) => (
|
||||
<UserRow
|
||||
key={user.id}
|
||||
name={user.name}
|
||||
|
|
@ -99,8 +98,7 @@ class Users extends Component<WithStyles<'wrapper'> & Stores<'userStore'>> {
|
|||
fDelete={() => (this.deleteId = user.id)}
|
||||
fEdit={() => (this.editId = user.id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@
|
|||
"noEmit": true,
|
||||
"module": "esnext",
|
||||
"resolveJsonModule": true,
|
||||
"keyofStringsOnly": true
|
||||
"keyofStringsOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
|
|
|||
6794
ui/yarn.lock
6794
ui/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue