mirror of
https://github.com/wassname/talk.git
synced 2026-08-10 12:41:02 +08:00
[next] Report Comment + Stream Refactor & Test Improvements (#2144)
* fix: stabilize mutations * feat: Refactor Stream + Implement Report Comment * test: add unit tests * chore: Improve stream integration tests * test: add integration test for reaction * test: add feature/integration tests for report comment * fix: order import * fix: performance issues + keep active button styling until clicked away
This commit is contained in:
@@ -13,8 +13,14 @@ export const VALIDATION_REQUIRED = () => (
|
||||
);
|
||||
|
||||
export const VALIDATION_TOO_SHORT = (minLength: number) => (
|
||||
<Localized id="framework-validation-tooShort">
|
||||
<span>{"This field must contain at least {$minLength} characters."}</span>
|
||||
<Localized id="framework-validation-tooShort" $minLength={minLength}>
|
||||
<span>{"Please enter at least {$minLength} characters."}</span>
|
||||
</Localized>
|
||||
);
|
||||
|
||||
export const VALIDATION_TOO_LONG = (maxLength: number) => (
|
||||
<Localized id="framework-validation-tooLong" $maxLength={maxLength}>
|
||||
<span>{"Please enter at max {$maxLength} characters."}</span>
|
||||
</Localized>
|
||||
);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
USERNAME_TOO_LONG,
|
||||
USERNAME_TOO_SHORT,
|
||||
VALIDATION_REQUIRED,
|
||||
VALIDATION_TOO_LONG,
|
||||
VALIDATION_TOO_SHORT,
|
||||
} from "./messages";
|
||||
|
||||
export type Validator<T = any, V = any> = (v: T, values: V) => ReactNode;
|
||||
@@ -76,6 +78,24 @@ export const validateURL = createValidator(
|
||||
INVALID_URL()
|
||||
);
|
||||
|
||||
/**
|
||||
* validateMinLength is a Validator that checks that the field has a min length of characters
|
||||
*/
|
||||
export const validateMinLength = (minLength: number) =>
|
||||
createValidator(
|
||||
v => !v || v.length >= minLength,
|
||||
VALIDATION_TOO_SHORT(minLength)
|
||||
);
|
||||
|
||||
/**
|
||||
* validateMaxLength is a Validator that checks that the field has max length of characters
|
||||
*/
|
||||
export const validateMaxLength = (maxLength: number) =>
|
||||
createValidator(
|
||||
v => !v || v.length <= maxLength,
|
||||
VALIDATION_TOO_LONG(maxLength)
|
||||
);
|
||||
|
||||
/**
|
||||
* validateUsernameMinLength is a Validator that checks that the username has a min length of characters
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
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.id) {
|
||||
return false;
|
||||
}
|
||||
return matchText(pattern, i.props.id, {
|
||||
collapseWhitespace: false,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export function getByID(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
return container.find(matcher(pattern, options));
|
||||
}
|
||||
|
||||
export function queryByID(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
const results = container.findAll(matcher(pattern, options));
|
||||
if (!results.length) {
|
||||
return null;
|
||||
}
|
||||
return results[0];
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export function queryAllByLabelText(
|
||||
options?: TextMatchOptions
|
||||
) {
|
||||
const matches = container.findAll(ariaLabelMatcher(pattern, options));
|
||||
queryAllByText(container, pattern).forEach(i => {
|
||||
queryAllByText(container, pattern, options).forEach(i => {
|
||||
if (typeof i.type !== "string") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from "react";
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import findParentsWithType from "./findParentsWithType";
|
||||
import findParentWithType from "./findParentWithType";
|
||||
import matchText, { TextMatchOptions, TextMatchPattern } from "./matchText";
|
||||
|
||||
const matcher = (pattern: TextMatchPattern, options?: TextMatchOptions) => (
|
||||
@@ -10,6 +12,12 @@ const matcher = (pattern: TextMatchPattern, options?: TextMatchOptions) => (
|
||||
if (typeof i.type !== "string") {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
i.props.dangerouslySetInnerHTML &&
|
||||
matchText(pattern, i.props.dangerouslySetInnerHTML.__html, options)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (!i.props.children) {
|
||||
return false;
|
||||
}
|
||||
@@ -22,20 +30,34 @@ const matcher = (pattern: TextMatchPattern, options?: TextMatchOptions) => (
|
||||
return false;
|
||||
};
|
||||
|
||||
interface SelectorOptions {
|
||||
selector?: string | React.ComponentClass<any> | React.StatelessComponent<any>;
|
||||
}
|
||||
|
||||
export function getByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
options?: TextMatchOptions & SelectorOptions
|
||||
) {
|
||||
return container.find(matcher(pattern, options));
|
||||
const result = findParentWithType(
|
||||
container.find(matcher(pattern, options)),
|
||||
options && options.selector
|
||||
);
|
||||
if (!result) {
|
||||
throw new Error(`Couldn't find text ${pattern}`);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getAllByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
options?: TextMatchOptions & SelectorOptions
|
||||
) {
|
||||
const results = container.findAll(matcher(pattern, options));
|
||||
const results = findParentsWithType(
|
||||
container.findAll(matcher(pattern, options)),
|
||||
options && options.selector
|
||||
);
|
||||
if (!results.length) {
|
||||
throw new Error(`Couldn't find text ${pattern}`);
|
||||
}
|
||||
@@ -45,9 +67,12 @@ export function getAllByText(
|
||||
export function queryByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
options?: TextMatchOptions & SelectorOptions
|
||||
) {
|
||||
const results = container.findAll(matcher(pattern, options));
|
||||
const results = findParentsWithType(
|
||||
container.findAll(matcher(pattern, options)),
|
||||
options && options.selector
|
||||
);
|
||||
if (!results.length) {
|
||||
return null;
|
||||
}
|
||||
@@ -57,7 +82,10 @@ export function queryByText(
|
||||
export function queryAllByText(
|
||||
container: ReactTestInstance,
|
||||
pattern: TextMatchPattern,
|
||||
options?: TextMatchOptions
|
||||
options?: TextMatchOptions & SelectorOptions
|
||||
) {
|
||||
return container.findAll(matcher(pattern, options));
|
||||
return findParentsWithType(
|
||||
container.findAll(matcher(pattern, options)),
|
||||
options && options.selector
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
export default function findParentWithType(
|
||||
instance: ReactTestInstance,
|
||||
selector:
|
||||
| string
|
||||
| React.ComponentClass<any>
|
||||
| React.StatelessComponent<any> = "*"
|
||||
): ReactTestInstance | null {
|
||||
if (selector === "*") {
|
||||
return instance;
|
||||
}
|
||||
if (instance.type === selector) {
|
||||
return instance;
|
||||
}
|
||||
if (instance.parent) {
|
||||
return findParentWithType(instance.parent, selector);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import findParentWithType from "./findParentWithType";
|
||||
|
||||
export default function findParentsWithType(
|
||||
instances: ReactTestInstance[],
|
||||
selector:
|
||||
| string
|
||||
| React.ComponentClass<any>
|
||||
| React.StatelessComponent<any> = "*"
|
||||
): ReactTestInstance[] {
|
||||
return instances
|
||||
.map(i => findParentWithType(i, selector))
|
||||
.filter(i => i) as ReactTestInstance[];
|
||||
}
|
||||
@@ -20,3 +20,5 @@ export { default as matchText } from "./matchText";
|
||||
export { default as toJSON } from "./toJSON";
|
||||
export { default as replaceHistoryLocation } from "./replaceHistoryLocation";
|
||||
export { default as createAuthToken } from "./createAuthToken";
|
||||
export { default as findParentsWithType } from "./findParentsWithType";
|
||||
export { default as findParentWithType } from "./findParentWithType";
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { ReactTestInstance } from "react-test-renderer";
|
||||
|
||||
import {
|
||||
getByID,
|
||||
queryByID,
|
||||
} from "./byID";
|
||||
import {
|
||||
getAllByLabelText,
|
||||
getByLabelText,
|
||||
@@ -47,6 +51,8 @@ export default function within(container: ReactTestInstance) {
|
||||
getAllByTestID: applyContainer(container, getAllByTestID),
|
||||
queryByTestID: applyContainer(container, queryByTestID),
|
||||
queryAllByTestID: applyContainer(container, queryAllByTestID),
|
||||
getByID: applyContainer(container, getByID),
|
||||
queryByID: applyContainer(container, queryByID),
|
||||
getByText: applyContainer(container, getByText),
|
||||
getAllByText: applyContainer(container, getAllByText),
|
||||
queryByText: applyContainer(container, queryByText),
|
||||
|
||||
Reference in New Issue
Block a user