mirror of
https://github.com/wassname/talk.git
synced 2026-08-14 12:50:17 +08:00
[next] Moderate (#2118)
* fix: load .env before building / watching * feat: Implement AppBar, Brand, and SubBar * feat: add card ui component * feat: add modqueue components * feat: implement modqueue * feat: add translations * test: add unit tests * feat: single comment view * test: feature / integration tests for modqueue * test: fix remaining tests * feature: support TextMatchOptions * fix: remove body count marker * fix: remove accidently added package * feat: testHelper toJSON * chore: cleanup + comments * chore: better types * test: fix test * chore: refactor decision history test * chore: tiny fix * fix: adjust to recent server changes * fix: marking suspect and banned words * feat: added moderation queue edge to accept/reject comment payloads - Simplified moderationQueue returns - Simplified resolvers * feat: update counts * feat: added id's to moderation queue and settings * fix+test: test count changes, apply fix * chore: adapt to server change, and remove custom mutation handlers * fix: use common utils * fix: purify fix, babel fix * fix: workaround css treeshake issue and upgrade css plugins * fix: fixed snapshot * fix: support empty word lists * feat: separate client config
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import matchText, { TextMatchOptions, TextMatchPattern } from "./matchText";
|
||||
|
||||
const matcher = (pattern: TextMatchPattern, options?: TextMatchOptions) => (
|
||||
i: ReactTestInstance
|
||||
) => {
|
||||
// Only look at dom components.
|
||||
if (typeof i.type !== "string" || !i.props["aria-label"]) {
|
||||
return false;
|
||||
}
|
||||
return matchText(pattern, i.props["aria-label"], {
|
||||
collapseWhitespace: false,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export function getByLabelText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.find(matcher(pattern, options));
|
||||
}
|
||||
|
||||
export function queryByLabelText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
try {
|
||||
return container.find(matcher(pattern, options));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function queryAllByLabelText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
try {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllByLabelText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import matchText, { TextMatchOptions, TextMatchPattern } from "./matchText";
|
||||
|
||||
const matcher = (pattern: TextMatchPattern, options?: TextMatchOptions) => (
|
||||
i: ReactTestInstance
|
||||
) => {
|
||||
// Only look at dom components.
|
||||
if (typeof i.type !== "string" || !i.props["data-test"]) {
|
||||
return false;
|
||||
}
|
||||
return matchText(pattern, i.props["data-test"], {
|
||||
collapseWhitespace: false,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export function getByTestID(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.find(matcher(pattern, options));
|
||||
}
|
||||
|
||||
export function queryByTestID(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
try {
|
||||
return container.find(matcher(pattern, options));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function queryAllByTestID(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
try {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllByTestID(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from "react";
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import matchText, { TextMatchOptions, TextMatchPattern } from "./matchText";
|
||||
|
||||
const matcher = (pattern: TextMatchPattern, options?: TextMatchOptions) => (
|
||||
i: ReactTestInstance
|
||||
) => {
|
||||
// Only look at dom components.
|
||||
if (typeof i.type !== "string") {
|
||||
return false;
|
||||
}
|
||||
if (!i.props.children) {
|
||||
return false;
|
||||
}
|
||||
const children = React.Children.toArray(i.props.children);
|
||||
for (const c of children) {
|
||||
if (typeof c === "string" && matchText(pattern, c, options)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export function getByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.find(matcher(pattern, options));
|
||||
}
|
||||
|
||||
export function queryByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
try {
|
||||
return container.find(matcher(pattern, options));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function queryAllByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
try {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
export default function getByTestID(id: string, instance: ReactTestInstance) {
|
||||
return instance.findByProps({ "data-test": id });
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from "react";
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
export default function getByText(text: string, instance: ReactTestInstance) {
|
||||
return instance.find(i => {
|
||||
if (!i.props.children) {
|
||||
return false;
|
||||
}
|
||||
const children = React.Children.toArray(i.props.children);
|
||||
for (const c of children) {
|
||||
if (
|
||||
typeof c === "string" &&
|
||||
c.toLowerCase().includes(text.toLowerCase())
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -13,7 +13,22 @@ export * from "./denormalize";
|
||||
export { default as replaceHistoryLocation } from "./replaceHistoryLocation";
|
||||
export { default as limitSnapshotTo } from "./limitSnapshotTo";
|
||||
export { default as inputPredicate } from "./inputPredicate";
|
||||
export { default as getByTestID } from "./getByTestID";
|
||||
export { default as getByText } from "./getByText";
|
||||
export {
|
||||
getByTestID,
|
||||
getAllByTestID,
|
||||
queryByTestID,
|
||||
queryAllByTestID,
|
||||
} from "./byTestID";
|
||||
export { getByText, getAllByText, queryByText, queryAllByText } from "./byText";
|
||||
export {
|
||||
getByLabelText,
|
||||
getAllByLabelText,
|
||||
queryByLabelText,
|
||||
queryAllByLabelText,
|
||||
} from "./byLabelText";
|
||||
export { default as within } from "./within";
|
||||
export { default as wait } from "./wait";
|
||||
export { default as waitForElement } from "./waitForElement";
|
||||
export { default as waitUntilThrow } from "./waitUntilThrow";
|
||||
export { default as matchText } from "./matchText";
|
||||
export { default as toJSON } from "./toJSON";
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
export interface TextMatchOptions {
|
||||
exact?: boolean; // defaults to true
|
||||
collapseWhitespace?: boolean; // defaults to true
|
||||
trim?: boolean; // defaults to true
|
||||
}
|
||||
|
||||
export type TextMatchPattern = string | RegExp;
|
||||
|
||||
export default function matchText(
|
||||
pattern: TextMatchPattern,
|
||||
text: string,
|
||||
options: TextMatchOptions = {}
|
||||
) {
|
||||
if (typeof pattern === "string") {
|
||||
let a = text;
|
||||
let b = pattern;
|
||||
if (options.trim || options.trim === undefined) {
|
||||
a = a.trim();
|
||||
b = b.trim();
|
||||
}
|
||||
if (
|
||||
options.collapseWhitespace ||
|
||||
options.collapseWhitespace === undefined
|
||||
) {
|
||||
a = a.replace(/\s+/g, " ");
|
||||
b = b.replace(/\s+/g, " ");
|
||||
}
|
||||
if (options.exact || options.exact === undefined) {
|
||||
return a === b;
|
||||
}
|
||||
return text.toLowerCase().includes(pattern.toLowerCase());
|
||||
}
|
||||
return pattern.test(text);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
interface ReactTestRendererJSON {
|
||||
type: string;
|
||||
props: { [propName: string]: any };
|
||||
children: null | ReactTestRendererNode[];
|
||||
$$typeof?: symbol; // Optional because we add it with defineProperty().
|
||||
}
|
||||
type ReactTestRendererNode = ReactTestRendererJSON | string;
|
||||
|
||||
export function toJSONRecursive(
|
||||
inst: ReactTestInstance
|
||||
): ReactTestRendererNode[] | null {
|
||||
const { children: _, ...props }: any = inst.props || {};
|
||||
let renderedChildren = null;
|
||||
if (inst.children) {
|
||||
for (const child of inst.children) {
|
||||
const renderedChild =
|
||||
typeof child === "string" ? [child] : toJSONRecursive(child);
|
||||
if (renderedChild !== null) {
|
||||
if (renderedChildren === null) {
|
||||
renderedChildren = [...renderedChild];
|
||||
} else {
|
||||
renderedChildren.push(...renderedChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof inst.type === "string") {
|
||||
const json: ReactTestRendererJSON = {
|
||||
type: inst.type,
|
||||
props,
|
||||
children: renderedChildren,
|
||||
};
|
||||
Object.defineProperty(json, "$$typeof", {
|
||||
value: Symbol.for("react.test.json"),
|
||||
});
|
||||
return [json];
|
||||
}
|
||||
return renderedChildren;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a ReactTestInstance into JSON for snapshotting purposes.
|
||||
*/
|
||||
export default function toJSON(
|
||||
inst: ReactTestInstance
|
||||
): ReactTestRendererNode | ReactTestRendererNode[] | null {
|
||||
const result = toJSONRecursive(inst);
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
if (result.length === 1) {
|
||||
return result[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import wait from "./wait";
|
||||
|
||||
interface Options {
|
||||
timeout?: number;
|
||||
interval?: number;
|
||||
}
|
||||
|
||||
export default async function waitUntilThrow(
|
||||
callback: () => ReactTestInstance | null,
|
||||
options?: Options
|
||||
): Promise<void> {
|
||||
await wait(() => {
|
||||
expect(callback).toThrow();
|
||||
}, options);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import {
|
||||
getAllByLabelText,
|
||||
getByLabelText,
|
||||
queryAllByLabelText,
|
||||
queryByLabelText,
|
||||
} from "./byLabelText";
|
||||
import {
|
||||
getAllByTestID,
|
||||
getByTestID,
|
||||
queryAllByTestID,
|
||||
queryByTestID,
|
||||
} from "./byTestID";
|
||||
import { getAllByText, getByText, queryAllByText, queryByText } from "./byText";
|
||||
import toJSON from "./toJSON";
|
||||
|
||||
type Func0<R> = () => R;
|
||||
type Func1<A, R> = (a?: A) => R;
|
||||
type Func2<A, B, R> = (a: A, b?: B) => R;
|
||||
type Func3<A, B, C, R> = (a: A, b: B, c?: C) => R;
|
||||
|
||||
type RemoveFirstArgument<T, R> =
|
||||
T extends [any, any, any, any?] ? Func3<T[1], T[2], T[3], R> :
|
||||
T extends [any, any, any?] ? Func2<T[1], T[2], R> :
|
||||
T extends [any, any?] ? Func1<T[1], R> :
|
||||
T extends [any] ? Func0<R> :
|
||||
unknown
|
||||
;
|
||||
|
||||
// tslint:disable
|
||||
// @TODO: currently tslint fails to parse this: `...any[]`.
|
||||
function applyContainer<T extends [ReactTestInstance, ...any[]], R>(container: ReactTestInstance, fn: (...args: T) => R): RemoveFirstArgument<T, R> {
|
||||
return ((...args: any[]) => fn(...[container, ...args] as any)) as any;
|
||||
}
|
||||
// tslint:enable
|
||||
|
||||
export default function within(container: ReactTestInstance) {
|
||||
return {
|
||||
getByTestID: applyContainer(container, getByTestID),
|
||||
getAllByTestID: applyContainer(container, getAllByTestID),
|
||||
queryByTestID: applyContainer(container, queryByTestID),
|
||||
queryAllByTestID: applyContainer(container, queryAllByTestID),
|
||||
getByText: applyContainer(container, getByText),
|
||||
getAllByText: applyContainer(container, getAllByText),
|
||||
queryByText: applyContainer(container, queryByText),
|
||||
queryAllByText: applyContainer(container, queryAllByText),
|
||||
getByLabelText: applyContainer(container, getByLabelText),
|
||||
getAllByLabelText: applyContainer(container, getAllByLabelText),
|
||||
queryByLabelText: applyContainer(container, queryByLabelText),
|
||||
queryAllByLabelText: applyContainer(container, queryAllByLabelText),
|
||||
toJSON: () => toJSON(container),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user