vue#reactive TypeScript Examples
The following examples show how to use
vue#reactive.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Store.ts From universal-model-vue with MIT License | 6 votes |
constructor(initialState: T, selectors?: Selectors<T, U>) {
this.reactiveState = reactive(initialState);
this.reactiveSelectors = {} as ComputedSelectors<T, U>;
if (selectors) {
Object.keys(selectors).forEach(
(key: keyof U) =>
(this.reactiveSelectors[key] = computed(() =>
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
selectors[key](this.reactiveState)
))
);
}
}
Example #2
Source File: scope-element.ts From quantum-sheet with GNU General Public License v3.0 | 6 votes |
/*const imports = computed(() => {
variables.forEach((value, key) => {
if(value[0].getters.length > 0) {
.push()
}
});
})*/
private createVariableArray(name: string): ScopedVariable[] {
// First variable, used to import values from the scope above
const importerVariable: ScopedVariable = reactive({
position: computed(() => this.position.value),
index: 0,
data: shallowRef(),
getters: [],
})
const newVariableArray = reactive([importerVariable])
// Cleanup if unused
watch([() => newVariableArray.length, () => importerVariable.getters.length], ([variableArrayLength, gettersLength]) => {
if (variableArrayLength <= 1 && gettersLength == 0) {
this._variableMap.delete(name)
}
})
this._variableMap.set(name, newVariableArray)
return newVariableArray
}
Example #3
Source File: useParent.ts From elenext with MIT License | 6 votes |
useChildren = <T extends Record<string, unknown>>(key: InjectionKey<ParentProvide<T>>, data: T) => {
const children: ComponentInternalInstance[] = reactive([])
provide(key, {
...data,
children,
insert: (child: ComponentInternalInstance) => {
children.push(child)
},
remove: (child: ComponentInternalInstance) => {
const index = children.indexOf(child)
children.splice(index, 1)
}
})
}
Example #4
Source File: config.ts From elenext with MIT License | 6 votes |
EConfigProvider = defineComponent({
name: '',
props: { theme: vptypes.object() },
setup(props, { slots }) {
const config = reactive({
theme: props.theme,
})
provide(EConfigProvider_IJK, config)
return slots.defalut?.()
},
})
Example #5
Source File: useHistory.ts From vhook with MIT License | 6 votes |
export function useHistory(): IHistoryResult {
const state: UnwrapRef<IHistoryState> = reactive(buildState())
const [, clearPopStateListener] = useEvent('popstate', buildState)
const [, clearPushStateListener] = useEvent('pushstate' as any, buildState)
const [, clearReplaceStateListener] = useEvent('replacestate' as any, buildState)
const clear = () => {
clearPopStateListener()
clearPushStateListener()
clearReplaceStateListener()
}
return {
...toRefs(state),
clear
}
}
Example #6
Source File: use-compitable.spec.ts From vooks with MIT License | 6 votes |
describe('# useCompitable', () => {
it('works', () => {
const props: {
oldKey?: number
newKey?: number
} = reactive({
oldKey: undefined,
newKey: 1
})
const compitableRef = useCompitable(props, ['oldKey', 'newKey'])
expect(compitableRef.value).toEqual(1)
props.oldKey = 2
expect(compitableRef.value).toEqual(2)
props.oldKey = undefined
props.newKey = undefined
expect(compitableRef.value).toEqual(undefined)
})
})
Example #7
Source File: state.ts From vue-keycloak with Apache License 2.0 | 6 votes |
state = reactive<KeycloakState>({
isAuthenticated: false,
hasFailed: false,
isPending: false,
token: '',
decodedToken: {},
username: '',
roles: [] as string[],
resourceRoles: {},
})
Example #8
Source File: app.ts From vite-plugin-ssr with MIT License | 5 votes |
function createApp(pageContext: PageContext) {
const { Page } = pageContext
let rootComponent: Component
const PageWithWrapper = defineComponent({
data: () => ({
Page: markRaw(Page),
pageProps: markRaw(pageContext.pageProps || {}),
}),
created() {
rootComponent = this
},
render() {
return h(
PageShell,
{},
{
default: () => {
return h(this.Page, this.pageProps)
},
},
)
},
})
const app = createSSRApp(PageWithWrapper)
// We use `app.changePage()` to do Client Routing, see `_default.page.client.js`
objectAssign(app, {
changePage: (pageContext: PageContext) => {
Object.assign(pageContextReactive, pageContext)
rootComponent.Page = markRaw(pageContext.Page)
rootComponent.pageProps = markRaw(pageContext.pageProps || {})
},
})
// When doing Client Routing, we mutate pageContext (see usage of `app.changePage()` in `_default.page.client.js`).
// We therefore use a reactive pageContext.
const pageContextReactive = reactive(pageContext)
// Make `pageContext` accessible from any Vue component
setPageContext(app, pageContextReactive)
return app
}
Example #9
Source File: store.ts From ncov with MIT License | 5 votes |
globalState = reactive({
cancelNextNavigation: false,
})
Example #10
Source File: scope-element.ts From quantum-sheet with GNU General Public License v3.0 | 5 votes |
private readonly _variableMap = reactive(new Map<string, ScopedVariable[]>())
Example #11
Source File: scope-element.ts From quantum-sheet with GNU General Public License v3.0 | 5 votes |
addGetter(name: string, position: ComputedRef<Vector2>): UseScopedGetter {
const getter: ScopedGetter = reactive({
position: position,
variable: undefined,
})
const data = computed(() => getter.variable?.data)
const variableArray = this.variableMap.get(name) ?? this.createVariableArray(name)
watchImmediate(
() => getter.position,
(value) => {
if (getter.variable) {
// If the getter is still in the correct position, bail out
const nextVariable = arrayUtils.at(variableArray, getter.variable.index + 1)
if (
isInRange(value, {
start: getter.variable.position,
end: nextVariable?.position,
})
) {
return
}
// Remove getter from old variable
arrayUtils.remove(getter.variable.getters, getter)
getter.variable = undefined
}
const { index } = arrayUtils.getBinaryInsertIndex(variableArray, (v) => v.position.compareTo(value))
const variable = arrayUtils.at(variableArray, index - 1)
assert(variable, `Getter position ${getter.position} outside of block ${this.position}`)
// Add getter to variable
variable.getters.push(getter)
getter.variable = variable
}
)
function remove() {
if (!getter.variable) return
arrayUtils.remove(getter.variable.getters, getter)
getter.variable = undefined
}
return {
data,
remove,
}
}
Example #12
Source File: scope-element.ts From quantum-sheet with GNU General Public License v3.0 | 5 votes |
addVariable(name: string, position: ComputedRef<Vector2>): UseScopedVariable {
// Add variable
const variable: ScopedVariable = reactive({
position: position,
index: -1,
data: shallowRef<any>(null),
getters: [],
})
const variableArray = this.variableMap.get(name) ?? this.createVariableArray(name)
watchImmediate(
() => variable.position,
(value) => {
// Remove (or bail out)
if (variable.index >= 0) {
assert(variableArray[variable.index] == variable, `Expected variable ${variable} to be in ${variableArray} at index ${variable.index}`)
const prev = arrayUtils.at(variableArray, variable.index - 1)
const next = arrayUtils.at(variableArray, variable.index + 1)
if (isInRange(value, { start: prev?.position, end: next?.position })) {
// TODO: Optimize?
// Currently this doesn't account for moving a variable past its getters
// return
}
removeVariable(variableArray, variable)
}
// Add
const { index } = arrayUtils.getBinaryInsertIndex(variableArray, (v) => v.position.compareTo(value))
const prev = arrayUtils.at(variableArray, index - 1)
// Take some getters from prev
if (prev?.getters) {
// Warning: This has to be strictly less than 0. If they are on the same spot, the getter belongs to the previous variable
variable.getters = prev.getters.filter((v) => value.compareTo(v.position) < 0)
variable.getters.forEach((v) => {
v.variable = variable
})
prev.getters = prev.getters.filter((v) => !(value.compareTo(v.position) < 0))
}
// Update variable indices
for (let i = index; i < variableArray.length; i++) {
variableArray[i].index = i + 1
}
variableArray.splice(index, 0, variable)
variable.index = index
}
)
function setData(data: Expression | null | undefined) {
variable.data = data
}
function remove() {
removeVariable(variableArray, variable)
}
return {
setData,
remove,
}
}
Example #13
Source File: index.ts From vue3-gettext with MIT License | 5 votes |
export function createGettext(options: Partial<GetTextOptions> = {}) {
Object.keys(options).forEach((key) => {
if (Object.keys(defaultOptions).indexOf(key) === -1) {
throw new Error(`${key} is an invalid option for the translate plugin.`);
}
});
const mergedOptions = {
...defaultOptions,
...options,
};
const translations = ref(normalizeTranslations(mergedOptions.translations));
const gettext: Language = reactive({
available: mergedOptions.availableLanguages,
muted: mergedOptions.mutedLanguages,
silent: mergedOptions.silent,
translations: computed({
get: () => {
return translations.value;
},
set: (val: GetTextOptions["translations"]) => {
translations.value = normalizeTranslations(val);
},
}),
current: mergedOptions.defaultLanguage,
install(app: App) {
// TODO: is this needed?
(app as any)[GetTextSymbol] = gettext;
app.provide(GetTextSymbol, gettext);
if (mergedOptions.setGlobalProperties) {
const globalProperties = app.config.globalProperties;
globalProperties.$gettext = gettext.$gettext;
globalProperties.$pgettext = gettext.$pgettext;
globalProperties.$ngettext = gettext.$ngettext;
globalProperties.$npgettext = gettext.$npgettext;
globalProperties.$gettextInterpolate = gettext.interpolate;
globalProperties.$language = gettext;
}
if (mergedOptions.provideDirective) {
app.directive("translate", Directive(gettext));
}
if (mergedOptions.provideComponent) {
// eslint-disable-next-line vue/multi-word-component-names, vue/component-definition-name-casing
app.component("translate", Component);
}
},
}) as unknown as Language;
const translate = translateRaw(gettext);
const interpolate = interpolateRaw(gettext);
gettext.$gettext = translate.gettext.bind(translate);
gettext.$pgettext = translate.pgettext.bind(translate);
gettext.$ngettext = translate.ngettext.bind(translate);
gettext.$npgettext = translate.npgettext.bind(translate);
gettext.interpolate = interpolate.bind(interpolate);
gettext.directive = Directive(gettext);
gettext.component = Component;
return gettext;
}
Example #14
Source File: index.ts From jz-gantt with MIT License | 5 votes |
initStore = () => {
const GtData = reactive(new GanttData()) as GanttData;
provide(Variables.provider.gtData, GtData);
const GtParam = reactive(new ParamData()) as ParamData;
provide(Variables.provider.gtParam, GtParam);
const rootEmit = ref();
provide(Variables.provider.gtRootEmit, rootEmit);
const rootRef = ref<HTMLDivElement>();
provide(Variables.provider.gtRootRef, rootRef);
const ganttRef = ref<HTMLDivElement>();
provide(Variables.provider.gtGanttRef, ganttRef);
const tableRef = ref<HTMLDivElement>();
provide(Variables.provider.gtTableRef, tableRef);
const isShowMask = ref(false);
provide(Variables.provider.gtIsShowMask, isShowMask);
const showDateList = reactive<Date[]>([]);
provide(Variables.provider.gtShowDateList, showDateList);
const successBarList: Row[] = reactive([]);
provide(Variables.provider.gtSuccessBarList, successBarList);
const initGanttWidth = ref(0);
provide(Variables.provider.gtInitGanttWidth, initGanttWidth);
// 设置移动线
const columnSliderLineVisible = ref(false);
provide(
Variables.provider.gtColumnSliderLineVisible,
columnSliderLineVisible
);
const columnSliderLineLeft = ref(0);
provide(Variables.provider.gtColumnSliderLineLeft, columnSliderLineLeft);
const columnDefaultLeft = ref(-1);
provide(Variables.provider.gtColumnDefaultLeft, columnDefaultLeft);
const timeout = 500; // 提示条消失时间
provide(Variables.provider.gtSuccessBarTimeout, timeout);
// toast
const isShowToast = ref(false);
provide(Variables.provider.gtIsShowToast, isShowToast);
const toastMessage = ref('');
provide(Variables.provider.gtToastMessage, toastMessage);
const toastQueue: any[] = [];
provide(Variables.provider.gtToastQueue, toastQueue);
// wheel
const scrollTop = ref(0);
provide(Variables.provider.gtScrollTop, scrollTop);
const rootHeight = ref(0);
provide(Variables.provider.gtRootHeight, rootHeight);
const scrollBarHeight = ref(Variables.size.defaultScrollBarHeight);
provide(Variables.provider.gtScrollBarHeight, scrollBarHeight);
const stopClickEvent = ref(false);
provide(Variables.provider.gtStopClickEvent, stopClickEvent);
}
Example #15
Source File: FormKitSchema.ts From formkit with MIT License | 5 votes |
FormKitSchema = defineComponent({
name: 'FormKitSchema',
props: {
schema: {
type: [Array, Object] as PropType<
FormKitSchemaNode[] | FormKitSchemaCondition
>,
required: true,
},
data: {
type: Object as PropType<Record<string, any>>,
default: () => ({}),
},
library: {
type: Object as PropType<FormKitComponentLibrary>,
default: () => ({}),
},
},
setup(props, context) {
const instance = getCurrentInstance()
let instanceKey = Symbol(String(i++))
instanceScopes.set(instanceKey, [])
let provider = parseSchema(props.library, props.schema)
let render: RenderChildren
let data: Record<string, any>
// Re-parse the schema if it changes:
watch(
() => props.schema,
(newSchema, oldSchema) => {
instanceKey = Symbol(String(i++))
provider = parseSchema(props.library, props.schema)
render = createRenderFn(provider, data, instanceKey)
if (newSchema === oldSchema) {
// In this edge case, someone pushed/modified something in the schema
// and we've successfully re-parsed, but since the schema is not
// referenced in the render function it technically isnt a dependency
// and we need to force a re-render since we swapped out the render
// function completely.
;(instance?.proxy?.$forceUpdate as unknown as CallableFunction)()
}
},
{ deep: true }
)
// Watch the data object explicitly
watchEffect(() => {
data = Object.assign(reactive(props.data), {
slots: context.slots,
})
render = createRenderFn(provider, data, instanceKey)
})
return () => render()
},
})
Example #16
Source File: provider.ts From fect with MIT License | 5 votes |
createProvider = <
// eslint-disable-next-line
T extends ComponentPublicInstance = ComponentPublicInstance<{}, any>,
ProvideValue = never
>(
key: InjectionKey<ProvideValue>
) => {
const publicChildren: T[] = reactive([])
const internalChildren: ComponentInternalInstance[] = reactive([])
const parent = getCurrentInstance()!
const provider = (value?: ProvideValue) => {
const link = (child: ComponentInternalInstance) => {
if (child.proxy) {
internalChildren.push(child)
publicChildren.push(child.proxy as T)
sortChildren(parent, publicChildren, internalChildren)
}
}
const unlink = (child: ComponentInternalInstance) => {
const idx = internalChildren.indexOf(child)
publicChildren.splice(idx, 1)
internalChildren.splice(idx, 1)
}
provide(
key,
Object.assign(
{
link,
unlink,
children: publicChildren,
internalChildren
},
value
)
)
}
return {
children: publicChildren,
provider
}
}
Example #17
Source File: useTree.spec.ts From vue3-treeview with MIT License | 5 votes |
describe("test useTree", () => {
let useTest = null;
let props = null;
const fakeEmit = (evt: string, ...args: any[]) => {};
const v = require("vue");
v.onUnmounted = jest.fn();
const spy = jest.spyOn(v, "provide").mockImplementation(() => () => {});
beforeEach(() => {
props = reactive({
config: {
root: ["id1"]
},
nodes: {
id1: {
text: "test"
}
}
});
useTest = useTree(props, fakeEmit);
});
it("Expect to call provide", () => {
expect(spy).toBeCalledWith("emitter", fakeEmit);
});
it("Expect to create element ref", () => {
expect(isRef(useTest.element)).toBeTruthy();
expect(useTest.element.value).toBeNull();
});
it("Expect to have basic style", () => {
expect(useTest.style.value).toMatchObject({
"align-items": "center",
"display": "flex"
});
});
});
Example #18
Source File: useSheetUpdate.ts From S2 with MIT License | 5 votes |
useSheetUpdate = (
s2Ref: ShallowRef<SpreadSheet | undefined>,
props: BaseSheetProps,
) => {
const updateFlag = reactive({
rerender: false,
reloadData: false,
rebuildDataset: false,
});
watch(
() => props.options,
(options, prevOptions) => {
updateFlag.rerender = true;
if (!Object.is(prevOptions?.hierarchyType, options?.hierarchyType)) {
// 自定义树目录需要重新构建 CustomTreePivotDataSet
updateFlag.reloadData = true;
updateFlag.rebuildDataset = true;
}
s2Ref.value?.setOptions(options as S2Options);
s2Ref.value?.changeSheetSize(options?.width, options?.height);
},
{ deep: isProxy(props.options) },
);
watch(
() => props.dataCfg!,
(dataCfg) => {
updateFlag.rerender = true;
updateFlag.reloadData = true;
s2Ref.value?.setDataCfg(dataCfg);
},
{ deep: isProxy(props.dataCfg) },
);
watch(
() => props.themeCfg!,
(themeCfg) => {
updateFlag.rerender = true;
s2Ref.value?.setThemeCfg(themeCfg);
},
{
deep: isProxy(props.themeCfg),
},
);
watch(updateFlag, (flag) => {
if (!flag.rerender) {
return;
}
s2Ref.value?.render(flag.reloadData, {
reBuildDataSet: flag.rebuildDataset,
});
flag.rerender = false;
flag.reloadData = false;
flag.rebuildDataset = false;
});
}
Example #19
Source File: useBreakpoint.ts From elenext with MIT License | 5 votes |
screens = reactive<ScreenMap>({
// lg: true
})
Example #20
Source File: useIcon.spec.ts From vue3-treeview with MIT License | 5 votes |
describe("test use icon", () => {
const config = {
openedIcon: null,
closedIcon: null
};
const nodes = {};
const storeProps = reactive({
nodes,
config
});
let props = null;
let useFake = null;
let state = null;
let v = require("vue");
v.inject = jest.fn(() => state);
beforeEach(() => {
props = reactive({
isLeaf: ref(false)
});
state = states.get(createState(storeProps as any));
useFake = useIcon(props);
});
it("Expect to have default openIcon", () => {
expect(useFake.openedIcon.value).toMatchObject(defaultConfig.openedIcon);
});
it("Expect to have default closeIcon", () => {
expect(useFake.closedIcon.value).toMatchObject(defaultConfig.closedIcon);
});
it("Expec to have icons", () => {
expect(useFake.hasIcons.value).toBeTruthy();
});
it("Expect to use icons", () => {
expect(useFake.useIcons.value).toBeTruthy();
});
it("Expect to have fake no style", () => {
expect(useFake.fakeNodeStyle.value).toMatchObject({
height: "8px",
width: "8px"
});
});
it("Expect to set opened icons", () => {
config.openedIcon = {
test: "test"
};
expect(useFake.openedIcon.value).toMatchObject({
test: "test"
});
});
it("Expect to set closed icons", () => {
config.closedIcon = {
test: "test"
};
expect(useFake.closedIcon.value).toMatchObject({
test: "test"
});
});
it("Expect not to use icons", () => {
props.isLeaf = true;
expect(useFake.useIcons.value).toBeFalsy();
});
});
Example #21
Source File: use-keyboard.ts From vooks with MIT License | 4 votes |
export default function useKeyboard (
options: useKeyboardOptions = {},
enabledRef?: Ref<boolean>
): Readonly<UseKeyboardState> {
const state = reactive<UseKeyboardState>({
ctrl: false,
command: false,
win: false,
shift: false,
tab: false
})
const {
keydown,
keyup
} = options
const keydownHandler = (e: KeyboardEvent): void => {
switch (e.key) {
case 'Control':
state.ctrl = true
break
case 'Meta':
state.command = true
state.win = true
break
case 'Shift':
state.shift = true
break
case 'Tab':
state.tab = true
break
}
if (keydown !== undefined) {
Object.keys(keydown).forEach(key => {
if (key !== e.key) return
const handler = keydown[key]
if (typeof handler === 'function') {
handler(e)
} else {
const { stop = false, prevent = false } = handler
if (stop) e.stopPropagation()
if (prevent) e.preventDefault()
handler.handler(e)
}
})
}
}
const keyupHandler = (e: KeyboardEvent): void => {
switch (e.key) {
case 'Control':
state.ctrl = false
break
case 'Meta':
state.command = false
state.win = false
break
case 'Shift':
state.shift = false
break
case 'Tab':
state.tab = false
break
}
if (keyup !== undefined) {
Object.keys(keyup).forEach(key => {
if (key !== e.key) return
const handler = keyup[key]
if (typeof handler === 'function') {
handler(e)
} else {
const { stop = false, prevent = false } = handler
if (stop) e.stopPropagation()
if (prevent) e.preventDefault()
handler.handler(e)
}
})
}
}
const setup = (): void => {
if (enabledRef === undefined || enabledRef.value) {
on('keydown', document, keydownHandler)
on('keyup', document, keyupHandler)
}
if (enabledRef !== undefined) {
watch(enabledRef, value => {
if (value) {
on('keydown', document, keydownHandler)
on('keyup', document, keyupHandler)
} else {
off('keydown', document, keydownHandler)
off('keyup', document, keyupHandler)
}
})
}
}
if (hasInstance()) {
onBeforeMount(setup)
onBeforeUnmount(() => {
if (enabledRef === undefined || enabledRef.value) {
off('keydown', document, keydownHandler)
off('keyup', document, keyupHandler)
}
})
} else {
setup()
}
return readonly(state)
}
Example #22
Source File: setting.ts From novel-downloader with GNU Affero General Public License v3.0 | 4 votes |
vm = createApp({
name: "nd-setting",
components: { "filter-tab": FilterTab, "log-ui": LogUI, "test-ui": TestUI },
setup() {
interface Setting {
enableDebug?: boolean;
enableTestPage?: boolean;
chooseSaveOption?: string;
filterSetting?: filterSettingGlobal;
currentTab: string;
}
const setting = reactive({} as Setting);
let settingBackup = {};
interface SaveOption {
key: string;
value: string;
options: globalSaveOptions;
}
const saveOptions: SaveOption[] = [
{ key: "null", value: "不使用自定义保存参数", options: {} },
{
key: "chapter_name",
value: "将章节名称格式修改为 第xx章 xxxx",
options: {
getchapterName: (chapter) => {
if (chapter.chapterName) {
return `第${chapter.chapterNumber.toString()}章 ${
chapter.chapterName
}`;
} else {
return `第${chapter.chapterNumber.toString()}章`;
}
},
},
},
{
key: "txt_space",
value: "txt文档每个自然段前加两个空格",
options: {
genChapterText: (chapterName, contentText) => {
contentText = contentText
.split("\n")
.map((line) => {
if (line.trim() === "") {
return line;
} else {
return line.replace(/^/, " ");
}
})
.join("\n");
return `## ${chapterName}\n\n${contentText}\n\n`;
},
},
},
{
key: "reverse_chapters",
value: "保存章节时倒序排列",
options: {
chapterSort: (a, b) => {
if (a.chapterNumber > b.chapterNumber) {
return -1;
}
if (a.chapterNumber === b.chapterNumber) {
return 0;
}
if (a.chapterNumber < b.chapterNumber) {
return 1;
}
return 0;
},
},
},
];
setting.enableDebug = enableDebug.value;
setting.chooseSaveOption = "null";
setting.enableTestPage = false;
setting.currentTab = "tab-1";
const curSaveOption = () => {
const _s = saveOptions.find((s) => s.key === setting.chooseSaveOption);
if (_s) {
return _s.options;
} else {
return saveOptions[0].options;
}
};
const saveFilter = (filterSetting: filterSettingGlobal) => {
setting.filterSetting = deepcopy(filterSetting);
};
const getFilterSetting = () => {
if (setting.filterSetting) {
return setting.filterSetting;
} else {
return;
}
};
provide("getFilterSetting", getFilterSetting);
const setConfig = (config: Setting) => {
setEnableDebug();
setCustomSaveOption();
setCustomFilter();
function setEnableDebug() {
if (typeof config.enableDebug === "boolean") {
config.enableDebug ? log.setLevel("trace") : log.setLevel("info");
enableDebug.value = config.enableDebug;
if (config.enableDebug) {
debug();
}
log.info(`[Init]enableDebug: ${enableDebug.value}`);
}
}
function setCustomSaveOption() {
(unsafeWindow as UnsafeWindow).saveOptions = curSaveOption();
}
function setCustomFilter() {
if (config.filterSetting) {
if (config.filterSetting.filterType === "null") {
(unsafeWindow as UnsafeWindow).chapterFilter = undefined;
} else {
const filterFunction = getFilterFunction(
config.filterSetting.arg,
config.filterSetting.functionBody
);
if (filterFunction) {
(unsafeWindow as UnsafeWindow).chapterFilter = (
chapter: Chapter
) => {
if (chapter.status === Status.aborted) {
return false;
}
return filterFunction(chapter);
};
}
}
}
}
};
const openStatus = ref("false");
const openSetting = () => {
settingBackup = deepcopy(setting) as Setting;
openStatus.value = "true";
};
const closeSetting = (keep: PointerEvent | boolean) => {
if (keep === true) {
settingBackup = deepcopy(setting);
} else {
Object.assign(setting, settingBackup);
}
openStatus.value = "false";
};
const closeAndSaveSetting = async () => {
closeSetting(true);
await sleep(30);
setConfig(deepcopy(setting));
log.info("[Init]自定义设置:" + JSON.stringify(setting));
};
return {
openStatus,
openSetting,
closeSetting,
closeAndSaveSetting,
saveFilter,
setting,
saveOptions,
};
},
template: settingHtml,
}).mount(el)
Example #23
Source File: index.test.tsx From vue-request with MIT License | 4 votes |
describe('useRequest', () => { beforeAll(() => { jest.useFakeTimers('modern'); }); const successApi = 'http://example.com/200'; const failApi = 'http://example.com/404'; // mock fetch fetchMock.get(successApi, { data: 'success' }); fetchMock.get(failApi, 404); const originalError = console.error; beforeEach(() => { console.error = jest.fn(); // clear cache clearCache(); // clear global options clearGlobalOptions(); // clear listener RECONNECT_LISTENER.clear(); FOCUS_LISTENER.clear(); VISIBLE_LISTENER.clear(); }); afterEach(() => { console.error = originalError; }); test('should be defined', () => { expect(useRequest).toBeDefined(); }); test('should auto run', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data } = useRequest(request); return () => <button>{`data:${data.value}`}</button>; }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:success'); }); test('can be manually triggered', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data, run } = useRequest(request, { manual: true }); return () => ( <button onClick={() => run()}>{`data:${data.value}`}</button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:undefined'); await wrapper.find('button').trigger('click'); await waitForAll(); expect(wrapper.text()).toBe('data:success'); }); test('params should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { run, params } = useRequest(request, { defaultParams: ['hello', 'world'], }); return () => ( <button onClick={() => run('hi there')}> {params.value?.join(',')} </button> ); }, }), ); await waitForTime(1000); expect(wrapper.text()).toBe('hello,world'); await wrapper.find('button').trigger('click'); await waitForTime(1000); expect(wrapper.text()).toEqual('hi there'); }); test('defaultParams should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data } = useRequest(request, { defaultParams: ['hello', 'world'], }); return () => <button>{`data:${data.value}`}</button>; }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:hello,world'); }); test('run can be accept params', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data, run } = useRequest(request); return () => ( <button onClick={() => run('hello', 'world')}> {`data:${data.value}`} </button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:success'); await wrapper.find('button').trigger('click'); await waitForAll(); expect(wrapper.text()).toBe('data:hello,world'); }); test('mutate should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data, mutate } = useRequest(request); return () => ( <button onClick={() => mutate('ok')}>{`data:${data.value}`}</button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:success'); await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('data:ok'); }); test('mutate callback should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data, mutate } = useRequest(request); return () => ( <button onClick={() => mutate(() => 'ok')}> {`data:${data.value}`} </button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:success'); await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('data:ok'); }); test('refresh should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { refresh, loading } = useRequest(request); return () => ( <button onClick={() => refresh()}> {`loading:${loading.value}`} </button> ); }, }), ); await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('loading:true'); await waitForAll(); expect(wrapper.text()).toBe('loading:false'); }); test('log request error by default', async () => { console.error = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest(failedRequest, { manual: true }); const handleClick = () => run(); return () => <button onClick={handleClick}></button>; }, }), ); await wrapper.find('button').trigger('click'); await waitForAll(); expect(console.error).toHaveBeenCalledWith(new Error('fail')); }); test('onSuccess should work', async () => { const mockSuccessCallback = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest(request, { manual: true, onSuccess: mockSuccessCallback, }); const handleClick = () => run(); return () => <button onClick={handleClick}></button>; }, }), ); await wrapper.find('button').trigger('click'); await waitForAll(); expect(mockSuccessCallback).toHaveBeenCalledWith('success', []); }); test('onError should work', async () => { const mockErrorCallback = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest(failedRequest, { manual: true, onError: mockErrorCallback, }); const handleClick = () => run(); return () => <button onClick={handleClick}></button>; }, }), ); await wrapper.find('button').trigger('click'); await waitForAll(); expect(mockErrorCallback).toHaveBeenCalledWith(new Error('fail'), []); }); test('initData should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data } = useRequest(request, { initialData: 'init', }); return () => <button>{`data:${data.value}`}</button>; }, }), ); expect(wrapper.text()).toBe('data:init'); await waitForAll(); expect(wrapper.text()).toBe('data:success'); }); test('ready should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const readyRef = ref(false); const { data } = useRequest(request, { ready: readyRef, }); return () => ( <button onClick={() => { readyRef.value = true; }} > {`data:${data.value}`} </button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:undefined'); await wrapper.find('button').trigger('click'); await waitForAll(); expect(wrapper.text()).toBe('data:success'); }); test('ready should save the first time request params : case 1', async () => { const wrapper = shallowMount( defineComponent({ setup() { const readyRef = ref(false); const { data, run } = useRequest(request, { ready: readyRef, defaultParams: ['default'], }); return () => ( <div> <button id="run" onClick={() => run('run')} /> <button id="ready" onClick={() => { readyRef.value = true; }} /> <span id="text">{`data:${data.value}`}</span> </div> ); }, }), ); await waitForAll(); expect(wrapper.find('#text').text()).toBe('data:undefined'); await wrapper.find('#ready').trigger('click'); await waitForAll(); expect(wrapper.find('#text').text()).toBe('data:default'); await wrapper.find('#run').trigger('click'); await waitForAll(); expect(wrapper.find('#text').text()).toBe('data:run'); }); test('ready should save the first time request params : case 2', async () => { const wrapper = shallowMount( defineComponent({ setup() { const readyRef = ref(false); const { data, run } = useRequest(request, { ready: readyRef, defaultParams: ['default'], }); return () => ( <div> <button id="run" onClick={() => run('run')} /> <button id="ready" onClick={() => { readyRef.value = true; }} /> <span id="text">{`data:${data.value}`}</span> </div> ); }, }), ); await waitForAll(); expect(wrapper.find('#text').text()).toBe('data:undefined'); await wrapper.find('#run').trigger('click'); await wrapper.find('#ready').trigger('click'); await waitForAll(); expect(wrapper.find('#text').text()).toBe('data:run'); }); test('track ready when ready initial value is false', async () => { const wrapper = shallowMount( defineComponent({ setup() { const readyRef = ref(true); const count = ref(0); const { data, run } = useRequest(request, { ready: readyRef, defaultParams: [count.value], }); return () => ( <button onClick={() => { readyRef.value = !readyRef.value; count.value += 1; run(count.value); }} > {`data:${data.value}`} </button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:0'); await wrapper.find('button').trigger('click'); await waitForAll(); expect(wrapper.text()).toBe('data:1'); }); test('ready should work only once', async () => { const wrapper = shallowMount( defineComponent({ setup() { const readyRef = ref(false); const count = ref(0); const { data, run } = useRequest(request, { ready: readyRef, defaultParams: [count.value], }); return () => ( <button onClick={async () => { readyRef.value = !readyRef.value; count.value += 1; run(count.value); }} > {`data:${data.value}`} </button> ); }, }), ); await waitForAll(); expect(wrapper.text()).toBe('data:undefined'); await wrapper.find('button').trigger('click'); // first click await waitForAll(); expect(wrapper.text()).toBe('data:1'); await wrapper.find('button').trigger('click'); // second click await waitForAll(); expect(wrapper.text()).toBe('data:2'); }); test('formatResult should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { data } = useRequest(request, { formatResult: () => 'formatted', }); return () => <button>{`data:${data.value}`}</button>; }, }), ); expect(wrapper.text()).toBe('data:undefined'); await waitForAll(); expect(wrapper.text()).toBe('data:formatted'); }); test('refreshDeps should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const refreshRef = ref(0); const refreshReactive = reactive({ count: 0, }); const { loading } = useRequest(request, { refreshDeps: [refreshRef, () => refreshReactive.count], }); return () => ( <div> <div id="data">{String(loading.value)}</div> <button id="ref" onClick={() => { refreshRef.value++; }} /> <button id="reactive" onClick={() => { refreshReactive.count++; }} /> </div> ); }, }), ); await waitForTime(1000); expect(wrapper.find('#data').text()).toBe('false'); for (let index = 0; index < 100; index++) { await wrapper.find('#ref').trigger('click'); expect(wrapper.find('#data').text()).toBe('true'); await waitForTime(1000); expect(wrapper.find('#data').text()).toBe('false'); } for (let index = 0; index < 100; index++) { await wrapper.find('#reactive').trigger('click'); expect(wrapper.find('#data').text()).toBe('true'); await waitForTime(1000); expect(wrapper.find('#data').text()).toBe('false'); } }); test('loadingDelay should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading } = useRequest(request, { loadingDelay: 800, }); return () => <button>{`loading:${loading.value}`}</button>; }, }), ); expect(wrapper.text()).toBe('loading:false'); await waitForTime(800); expect(wrapper.text()).toBe('loading:true'); await waitForTime(200); expect(wrapper.text()).toBe('loading:false'); }); test('cancel loadingDelay should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, cancel } = useRequest(request, { loadingDelay: 800, }); return () => ( <button onClick={() => cancel()}> {`loading:${loading.value}`} </button> ); }, }), ); expect(wrapper.text()).toBe('loading:false'); await waitForTime(800); expect(wrapper.text()).toBe('loading:true'); await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('loading:false'); }); test('cancel should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { cancel, data, run } = useRequest(request); return () => ( <div> <button onClick={() => cancel()} id="cancel" /> <button onClick={() => run()} id="run" /> <span id="data">{`data:${data.value}`}</span> </div> ); }, }), ); expect(wrapper.find('#data').text()).toBe('data:undefined'); await wrapper.find('#cancel').trigger('click'); await waitForAll(); expect(wrapper.find('#data').text()).toBe('data:undefined'); await wrapper.find('#run').trigger('click'); await waitForAll(); expect(wrapper.find('#data').text()).toBe('data:success'); }); test('cancel should work when request error', async () => { console.error = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { data, run, cancel } = useRequest(failedRequest, { manual: true, }); return () => ( <div> <button id="run" onClick={() => run().catch(() => {})}></button>; <button id="cancel" onClick={() => cancel()}></button>; <span id="data">{`data:${data.value}`}</span> </div> ); }, }), ); expect(wrapper.find('#data').text()).toBe('data:undefined'); await wrapper.find('#run').trigger('click'); await waitForTime(200); await wrapper.find('#cancel').trigger('click'); await waitForAll(); expect(wrapper.find('#data').text()).toBe('data:undefined'); await wrapper.find('#run').trigger('click'); await waitForAll(); expect(console.error).toHaveBeenCalledWith(new Error('fail')); }); test('pollingInterval should work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, cancel } = useRequest(request, { pollingInterval: 500, }); return () => ( <button onClick={() => cancel()}> {`loading:${loading.value}`} </button> ); }, }), ); expect(wrapper.text()).toBe('loading:true'); await waitForTime(1000); expect(wrapper.text()).toBe('loading:false'); await waitForTime(500); expect(wrapper.text()).toBe('loading:true'); await waitForTime(1000); expect(wrapper.text()).toBe('loading:false'); await wrapper.find('button').trigger('click'); waitForTime(600); expect(wrapper.text()).toBe('loading:false'); }); test('pollingInterval less than 0 should not work', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, cancel } = useRequest(request, { pollingInterval: -0.1, }); return () => ( <button onClick={() => cancel()}> {`loading:${loading.value}`} </button> ); }, }), ); expect(wrapper.text()).toBe('loading:true'); await waitForTime(1000); expect(wrapper.text()).toBe('loading:false'); await waitForTime(10); expect(wrapper.text()).toBe('loading:false'); }); test('pollingWhenHidden be false should work', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data } = useRequest(() => request((count += 1)), { pollingInterval: 1000, pollingWhenHidden: false, }); return () => <button>{`data:${data.value}`}</button>; }, }), ); expect(wrapper.text()).toBe('data:undefined'); await waitForTime(1000); expect(wrapper.text()).toBe('data:1'); await waitForTime(2000); expect(wrapper.text()).toBe('data:2'); // mock tab hide Object.defineProperty(document, 'visibilityState', { value: 'hidden', writable: true, }); await waitForTime(2000); expect(wrapper.text()).toBe('data:3'); await waitForTime(2000); expect(wrapper.text()).toBe('data:3'); // mock tab show Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: true, }); jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:4'); await waitForTime(2000); expect(wrapper.text()).toBe('data:5'); }); test('pollingWhenHidden be true should work', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data } = useRequest(() => request((count += 1)), { pollingInterval: 1000, pollingWhenHidden: true, }); return () => <button>{`data:${data.value}`}</button>; }, }), ); expect(wrapper.text()).toBe('data:undefined'); await waitForTime(1000); expect(wrapper.text()).toBe('data:1'); await waitForTime(2000); expect(wrapper.text()).toBe('data:2'); // mock tab hide Object.defineProperty(document, 'visibilityState', { value: 'hidden', writable: true, }); await waitForTime(2000); expect(wrapper.text()).toBe('data:3'); await waitForTime(2000); expect(wrapper.text()).toBe('data:4'); // mock tab show Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: true, }); jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); // because pollingWhenHidden is true, so refresh never trigger expect(wrapper.text()).toBe('data:4'); await waitForTime(2000); expect(wrapper.text()).toBe('data:5'); }); test('refreshOnWindowFocus should work', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data, run } = useRequest(() => request((count += 1)), { refreshOnWindowFocus: true, }); return () => ( <button onClick={() => run()}>{`data:${data.value}`}</button> ); }, }), ); expect(wrapper.text()).toBe('data:undefined'); await waitForTime(1000); expect(wrapper.text()).toBe('data:1'); await wrapper.find('button').trigger('click'); await waitForTime(1000); expect(wrapper.text()).toBe('data:2'); // mock tab visible jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:3'); jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:3'); // wait for 5s await waitForTime(4000); jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:4'); }); test('refocusTimespan should work', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data, run } = useRequest(() => request((count += 1)), { refreshOnWindowFocus: true, refocusTimespan: 3000, }); return () => ( <button onClick={() => run()}>{`data:${data.value}`}</button> ); }, }), ); expect(wrapper.text()).toBe('data:undefined'); await waitForTime(1000); expect(wrapper.text()).toBe('data:1'); await wrapper.find('button').trigger('click'); await waitForTime(1000); expect(wrapper.text()).toBe('data:2'); // mock tab visible jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:3'); jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:3'); // wait for 3s await waitForTime(2000); jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1000); expect(wrapper.text()).toBe('data:4'); }); test('debounceInterval should work', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { debounceInterval: 100, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(1); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(2); }); test('debounceOptions should work: case 1', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { debounceInterval: 100, debounceOptions: { leading: true, trailing: false, }, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } expect(mockFn).toHaveBeenCalledTimes(1); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(1); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } expect(mockFn).toHaveBeenCalledTimes(2); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(2); }); test('debounceOptions should work: case 2', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { debounceInterval: 100, debounceOptions: { leading: false, trailing: false, }, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } expect(mockFn).toHaveBeenCalledTimes(0); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(0); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } expect(mockFn).toHaveBeenCalledTimes(0); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(0); }); test('debounceOptions should work: case 3', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { debounceInterval: 500, debounceOptions: { maxWait: 1000, }, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } expect(mockFn).toHaveBeenCalledTimes(5); await waitForTime(1000); expect(mockFn).toHaveBeenCalledTimes(5); for (let index = 0; index < 100; index++) { await wrapper.find('button').trigger('click'); await waitForTime(50); } expect(mockFn).toHaveBeenCalledTimes(10); await waitForTime(1000); expect(mockFn).toHaveBeenCalledTimes(10); }); test('debounceInterval should work with cancel', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run, cancel } = useRequest( () => { mockFn(); return request(); }, { debounceInterval: 100, manual: true, }, ); return () => ( <div> <button id="run" onClick={() => run()} /> <button id="cancel" onClick={() => cancel()} /> </div> ); }, }), ); const run = () => wrapper.find('#run').trigger('click'); const cancel = () => wrapper.find('#cancel').trigger('click'); for (let index = 0; index < 100; index++) { await run(); await waitForTime(50); } await cancel(); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(0); for (let index = 0; index < 100; index++) { await run(); await waitForTime(50); } await cancel(); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(0); }); test('initial auto run should skip debounce', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { debounceInterval: 100, }, ); return () => <button onClick={() => run()} />; }, }), ); expect(mockFn).toHaveBeenCalledTimes(1); await wrapper.find('button').trigger('click'); await waitForTime(50); expect(mockFn).toHaveBeenCalledTimes(1); await waitForTime(100); expect(mockFn).toHaveBeenCalledTimes(2); }); test('throttleInterval should work', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { throttleInterval: 100, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForAll(); // have been call 3 times // because the function will invoking on the leading edge and trailing edge of the timeout expect(mockFn).toHaveBeenCalledTimes(3); }); test('throttleOptions should work, case: 1', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { throttleInterval: 100, throttleOptions: { leading: false, }, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForAll(); // have been call 2 times // because the function will only invoking on the trailing edge of the timeout expect(mockFn).toHaveBeenCalledTimes(2); }); test('throttleOptions should work, case: 2', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { throttleInterval: 100, throttleOptions: { trailing: false, }, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForAll(); // have been call 2 times // because the function will only invoking on the leading edge of the timeout expect(mockFn).toHaveBeenCalledTimes(2); }); test('throttleOptions should work, case: 3', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run } = useRequest( () => { mockFn(); return request(); }, { throttleInterval: 100, throttleOptions: { leading: false, trailing: false, }, manual: true, }, ); return () => <button onClick={() => run()} />; }, }), ); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForTime(50); await wrapper.find('button').trigger('click'); await waitForAll(); expect(mockFn).toHaveBeenCalledTimes(0); }); test('throttleInterval should work with cancel', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run, cancel } = useRequest( () => { mockFn(); return request(); }, { throttleInterval: 100, manual: true, }, ); return () => ( <div> <button id="run" onClick={() => run()} /> <button id="cancel" onClick={() => cancel()} /> </div> ); }, }), ); const run = () => wrapper.find('#run').trigger('click'); const cancel = () => wrapper.find('#cancel').trigger('click'); await run(); // trigger by leading expect(mockFn).toHaveBeenCalledTimes(1); await waitForTime(10); await cancel(); await run(); // trigger by leading expect(mockFn).toHaveBeenCalledTimes(2); await waitForTime(10); await cancel(); await run(); // trigger by leading expect(mockFn).toHaveBeenCalledTimes(3); await waitForTime(50); await run(); await run(); await waitForAll(); // trigger by trailing expect(mockFn).toHaveBeenCalledTimes(4); }); test('cache should work', async () => { let count = 0; const TestComponent = defineComponent({ setup() { const { data, run } = useRequest(request, { cacheKey: 'cacheKey', cacheTime: 10000, }); return () => ( <button onClick={() => run((count += 1))}>{data.value}</button> ); }, }); let wrapper = shallowMount(TestComponent); expect(wrapper.find('button').text()).toBe(''); await waitForTime(1000); expect(wrapper.find('button').text()).toBe('success'); for (let index = 0; index < 5; index++) { await wrapper.find('button').trigger('click'); await waitForTime(1000); } expect(wrapper.find('button').text()).toBe('5'); wrapper.unmount(); // remount component wrapper = shallowMount(TestComponent); expect(wrapper.find('button').text()).toBe('5'); await waitForTime(1000); expect(wrapper.find('button').text()).toBe('5'); for (let index = 0; index < 5; index++) { await wrapper.find('button').trigger('click'); await waitForTime(1000); } expect(wrapper.find('button').text()).toBe('10'); wrapper.unmount(); // waiting for cache timeout waitForTime(10000); // remount component wrapper = shallowMount(TestComponent); expect(wrapper.find('button').text()).toBe(''); }); test('cache staleTime should work', async () => { let count = 0; const TestComponent = defineComponent({ setup() { const { data, run } = useRequest(request, { cacheKey: 'cacheKey', staleTime: 5000, }); return () => ( <button onClick={() => run((count += 1))}>{data.value}</button> ); }, }); let wrapper = shallowMount(TestComponent); expect(wrapper.find('button').text()).toBe(''); await waitForTime(1000); expect(wrapper.find('button').text()).toBe('success'); for (let index = 0; index < 5; index++) { await wrapper.find('button').trigger('click'); await waitForTime(1000); } expect(wrapper.find('button').text()).toBe('5'); wrapper.unmount(); // remount component wrapper = shallowMount(TestComponent); expect(wrapper.find('button').text()).toBe('5'); await waitForTime(1000); expect(wrapper.find('button').text()).toBe('5'); for (let index = 0; index < 5; index++) { await wrapper.find('button').trigger('click'); await waitForTime(1000); } expect(wrapper.find('button').text()).toBe('10'); wrapper.unmount(); // waiting for stale timeout jest.setSystemTime(new Date().getTime() + 5000); // remount component wrapper = shallowMount(TestComponent); expect(wrapper.find('button').text()).toBe('10'); await waitForTime(1000); expect(wrapper.find('button').text()).toBe('10'); }); test('queryKey should work : case 1', async () => { const wrapper = shallowMount( defineComponent({ setup() { // auto run with empty params const { loading, params, data } = useRequest(request, { queryKey: id => id, }); return () => ( <div> <div id="loading">{`${loading.value}`}</div> <div id="data">{`${data.value}`}</div> <div id="params">{`${params.value.length}`}</div> </div> ); }, }), ); expect(wrapper.find('#loading').text()).toBe('true'); await waitForTime(1000); expect(wrapper.find('#loading').text()).toBe('false'); expect(wrapper.find('#data').text()).toBe('success'); expect(wrapper.find('#params').text()).toBe('0'); }); test('queryKey should work : case 2', async () => { const users = [ { id: '1', username: 'A' }, { id: '2', username: 'B' }, { id: '3', username: 'C' }, ]; const wrapper = shallowMount( defineComponent({ setup() { const { run, queries, data, loading } = useRequest(request, { manual: true, queryKey: id => id, }); return () => ( <div> <div id="data">{data.value}</div> <div id="loading">{loading.value.toString()}</div> <ul> {users.map(item => ( <li key={item.id} id={item.username} onClick={() => run(item.id)} > {queries[item.id]?.loading ? 'loading' : item.username} </li> ))} </ul> </div> ); }, }), ); for (let i = 0; i < users.length; i++) { const userName = users[i].username; const currentId = users[i].id; await wrapper.find(`#${userName}`).trigger('click'); expect(wrapper.find(`#${userName}`).text()).toBe('loading'); expect(wrapper.find('#data').text()).toBe(''); expect(wrapper.find('#loading').text()).toBe('true'); await waitForTime(1000); expect(wrapper.find(`#${userName}`).text()).toBe(userName); expect(wrapper.find('#data').text()).toBe(currentId); expect(wrapper.find('#loading').text()).toBe('false'); } }); test('queryKey should work : case 3', async () => { // swr const users = [ { id: '1', username: 'A' }, { id: '2', username: 'B' }, { id: '3', username: 'C' }, ]; const Child = defineComponent({ setup() { const { run, queries } = useRequest(request, { queryKey: id => id, cacheKey: 'users', }); return () => ( <div> <ul id="child"> {users.map(item => ( <li key={item.id} id={item.username} onClick={() => run(item.id)} > {queries[item.id]?.loading ? 'loading' : item.username} </li> ))} </ul> </div> ); }, }); const Parent = mount( defineComponent({ props: { show: { type: Boolean, default: false, }, }, setup(props) { return () => <div>{props.show && <Child />}</div>; }, }), ); await Parent.setProps({ show: true, }); for (let i = 0; i < users.length; i++) { const userName = users[i].username; await Parent.find(`#${userName}`).trigger('click'); expect(Parent.find(`#${userName}`).text()).toBe('loading'); await waitForTime(1000); expect(Parent.find(`#${userName}`).text()).toBe(userName); } // unmount Child await Parent.setProps({ show: false, }); // remount Child await Parent.setProps({ show: true, }); // all queries will auto refresh for (let i = 0; i < users.length; i++) { const userName = users[i].username; expect(Parent.find(`#${userName}`).text()).toBe('loading'); } await waitForTime(1000); for (let i = 0; i < users.length; i++) { const userName = users[i].username; expect(Parent.find(`#${userName}`).text()).toBe(userName); } }); test('queryKey should work : case 4', async () => { const users = [ { id: '1', username: 'A' }, { id: '2', username: 'B' }, { id: '3', username: 'C' }, ]; const wrapper = shallowMount( defineComponent({ setup() { const { run, queries, data, loading } = useRequest(request, { manual: true, refreshOnWindowFocus: true, queryKey: id => id, }); return () => ( <div> <div id="data">{data.value}</div> <div id="loading">{loading.value.toString()}</div> <ul> {users.map(item => ( <li key={item.id} id={item.username} onClick={() => run(item.id)} > {queries[item.id]?.loading ? 'loading' : queries[item.id]?.data} </li> ))} </ul> </div> ); }, }), ); for (let i = 0; i < users.length; i++) { const userName = users[i].username; const currentId = users[i].id; await wrapper.find(`#${userName}`).trigger('click'); expect(wrapper.find(`#${userName}`).text()).toBe('loading'); expect(wrapper.find('#data').text()).toBe(''); expect(wrapper.find('#loading').text()).toBe('true'); await waitForTime(1000); expect(wrapper.find(`#${userName}`).text()).toBe(currentId); expect(wrapper.find('#data').text()).toBe(currentId); expect(wrapper.find('#loading').text()).toBe('false'); } // mock tab visible jsdom.window.dispatchEvent(new Event('visibilitychange')); await waitForTime(1); for (let i = 0; i < users.length; i++) { const userName = users[i].username; expect(wrapper.find(`#${userName}`).text()).toBe('loading'); } await waitForTime(1000); for (let i = 0; i < users.length; i++) { const userName = users[i].username; const currentId = users[i].id; expect(wrapper.find(`#${userName}`).text()).toBe(currentId); } }); test('queryKey should work with root level `cancel`, `mutate`, `refresh`', async () => { const users = [ { id: '1', username: 'A' }, { id: '2', username: 'B' }, { id: '3', username: 'C' }, ]; const wrapper = shallowMount( defineComponent({ setup() { const { run, queries, mutate, refresh, cancel } = useRequest( request, { manual: true, refreshOnWindowFocus: true, queryKey: id => id, }, ); return () => ( <div> <div id="mutate" onClick={() => mutate('new data')} /> <div id="refresh" onClick={() => refresh()} /> <div id="cancel" onClick={() => cancel()} /> <ul> {users.map(item => ( <li key={item.id} id={item.username} onClick={() => run(item.id)} > {queries[item.id]?.loading ? 'loading' : queries[item.id]?.data} </li> ))} </ul> </div> ); }, }), ); const mutate = () => wrapper.find('#mutate').trigger('click'); const refresh = () => wrapper.find('#refresh').trigger('click'); const cancel = () => wrapper.find('#cancel').trigger('click'); for (let i = 0; i < users.length; i++) { const userName = users[i].username; const currentId = users[i].id; const userElement = wrapper.find(`#${userName}`); await userElement.trigger('click'); expect(userElement.text()).toBe('loading'); await waitForTime(1000); expect(userElement.text()).toBe(currentId); await mutate(); expect(userElement.text()).toBe('new data'); await userElement.trigger('click'); expect(userElement.text()).toBe('loading'); await waitForTime(100); await cancel(); expect(userElement.text()).toBe('new data'); await refresh(); expect(userElement.text()).toBe('loading'); } }); test('errorRetry should work. case 1', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { run, loading } = useRequest(failedRequest, { manual: true, errorRetryCount: 2, errorRetryInterval: 1000, }); const handleClick = () => run(); return () => ( <button onClick={handleClick}>{`${loading.value}`}</button> ); }, }), ); for (let oIndex = 0; oIndex < 10; oIndex++) { await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('false'); // retrying for (let index = 0; index < 2; index++) { await waitForTime(1000); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('false'); } // stop retry await waitForTime(1000); expect(wrapper.text()).toBe('false'); } }); test('errorRetry should work. case 2', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, cancel } = useRequest(failedRequest, { errorRetryCount: 3, errorRetryInterval: 1000, }); return () => ( <button onClick={() => cancel()}>{`${loading.value}`}</button> ); }, }), ); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('false'); // first retry await waitForTime(1000); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('false'); // second retry await waitForTime(1000); expect(wrapper.text()).toBe('true'); // trigger cancel await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('false'); await waitForTime(1000); expect(wrapper.text()).toBe('false'); }); test('errorRetry should work. case 3', async () => { const mockFn = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { run, loading } = useRequest(failedRequest, { manual: true, errorRetryCount: 10, onError: () => mockFn(), }); const handleClick = () => run(); return () => ( <button onClick={handleClick}>{`${loading.value}`}</button> ); }, }), ); await wrapper.find('button').trigger('click'); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('false'); // retrying for (let index = 0; index < 10; index++) { await waitForAll(); expect(wrapper.text()).toBe('false'); } // stop retry await waitForAll(); expect(wrapper.text()).toBe('false'); expect(mockFn).toHaveBeenCalledTimes(11); }); test('errorRetry should work with debounce', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { run, loading, error } = useRequest(failedRequest, { manual: true, debounceInterval: 1000, errorRetryCount: 4, errorRetryInterval: 1000, }); const handleClick = () => run(); return () => ( <button onClick={handleClick}> {`${loading.value || error.value?.message}`} </button> ); }, }), ); // request await wrapper.find('button').trigger('click'); await waitForTime(2001); expect(wrapper.text()).toBe('fail'); // retrying 1 await waitForTime(1000); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); // retrying 2 await waitForTime(1000); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); // trigger button to reset retry count await wrapper.find('button').trigger('click'); await waitForTime(2001); expect(wrapper.text()).toBe('fail'); for (let index = 0; index < 4; index++) { await waitForTime(1000); expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); } await waitForTime(1000); expect(wrapper.text()).toBe('fail'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); }); test('errorRetry should work with pollingInterval', async () => { let flag = true; const mixinRequest = () => { return new Promise((resolve, reject) => { setTimeout(() => { if (flag) { resolve('success'); } else { reject(new Error('fail')); } }, 1000); }); }; const wrapper = shallowMount( defineComponent({ setup() { const { loading, error, data } = useRequest(mixinRequest, { errorRetryCount: 3, errorRetryInterval: 600, pollingInterval: 500, }); return () => ( <button> {`${loading.value || data.value || error.value?.message}`} </button> ); }, }), ); // normal API request for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('success'); await waitForTime(500); } // mock API error request flag = false; // retrying for (let index = 0; index < 3; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); await waitForTime(600); } // stop retry expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); await waitForTime(600); expect(wrapper.text()).toBe('fail'); }); test('pollingInterval always receive a error request', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, error } = useRequest(failedRequest, { pollingInterval: 1000, }); return () => ( <button>{`${loading.value || error.value?.message}`}</button> ); }, }), ); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); await waitForTime(1000); } }); test('pollingInterval always receive a error request and errorRetryCount is -1', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, error } = useRequest(failedRequest, { errorRetryCount: -1, pollingInterval: 500, errorRetryInterval: 600, }); return () => ( <button>{`${loading.value || error.value?.message}`}</button> ); }, }), ); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe('fail'); await waitForTime(600); } }); test('reset loadingDelay correctly when rerun or refresh', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { loading, run, refresh } = useRequest(request, { loadingDelay: 500, }); return () => ( <div> <div id="loading">{`${loading.value}`}</div> <button id="run" onClick={() => { run(); }} /> <button id="refresh" onClick={() => { refresh(); }} /> </div> ); }, }), ); const loadingRes = () => wrapper.find('#loading').text(); const run = () => wrapper.find('#run').trigger('click'); const refresh = () => wrapper.find('#refresh').trigger('click'); await waitForTime(300); expect(loadingRes()).toBe('false'); run(); await waitForTime(300); expect(loadingRes()).toBe('false'); await waitForTime(200); expect(loadingRes()).toBe('true'); refresh(); await waitForTime(300); expect(loadingRes()).toBe('false'); await waitForTime(200); expect(loadingRes()).toBe('true'); }); test('reset polling correctly when rerun or refresh', async () => { enum RequestType { run, refresh, polling, } const requestTypeRef = ref<RequestType>(RequestType.run); const runCountRef = ref(0); const refreshCountRef = ref(0); const pollingCountRef = ref(0); const expectCount = (param: Ref<number>, value: number) => { expect(param.value).toBe(value); }; const triggerWithCorrectType = (source: Function, type: RequestType) => { requestTypeRef.value = type; source(); }; const wrapper = shallowMount( defineComponent({ setup() { const { run, refresh } = useRequest( () => { switch (requestTypeRef.value) { case RequestType.polling: pollingCountRef.value += 1; break; case RequestType.run: runCountRef.value += 1; break; case RequestType.refresh: refreshCountRef.value += 1; break; } if ( requestTypeRef.value === RequestType.run || requestTypeRef.value === RequestType.refresh ) { requestTypeRef.value = RequestType.polling; } return request(); }, { pollingInterval: 500, }, ); return () => ( <div> <button id="run" onClick={() => { run(); }} /> <button id="refresh" onClick={() => { refresh(); }} /> </div> ); }, }), ); const run = () => wrapper.find('#run').trigger('click'); const refresh = () => wrapper.find('#refresh').trigger('click'); /* ------------------------------------- run ------------------------------------- */ expectCount(runCountRef, 1); expectCount(pollingCountRef, 0); // auto run await waitForTime(1000); for (let index = 1; index <= 100; index++) { // wait for polling await waitForTime(500); // request complete await waitForTime(1000); expectCount(runCountRef, 1); expectCount(pollingCountRef, index); } // polling is pending await waitForTime(200); triggerWithCorrectType(run, RequestType.run); await waitForTime(1000); expectCount(runCountRef, 2); expectCount(pollingCountRef, 100); for (let index = 1; index <= 100; index++) { // wait for polling await waitForTime(500); // request complete await waitForTime(1000); expectCount(pollingCountRef, index + 100); } /* ------------------------------------- refresh ------------------------------------- */ expectCount(runCountRef, 2); expectCount(refreshCountRef, 0); expectCount(pollingCountRef, 200); // polling is pending await waitForTime(200); triggerWithCorrectType(refresh, RequestType.refresh); expectCount(refreshCountRef, 1); expectCount(pollingCountRef, 200); // refresh complete await waitForTime(1000); for (let index = 1; index <= 100; index++) { // wait for polling await waitForTime(500); // request complete await waitForTime(1000); expectCount(pollingCountRef, index + 200); } expectCount(runCountRef, 2); expectCount(refreshCountRef, 1); expectCount(pollingCountRef, 300); }); test('reset error retry correctly when rerun or refresh', async () => { enum RequestType { run, refresh, errorRetry, } const requestTypeRef = ref<RequestType>(RequestType.run); const runCountRef = ref(0); const refreshCountRef = ref(0); const errorRetryCountRef = ref(0); const expectCount = (param: Ref<number>, value: number) => { expect(param.value).toBe(value); }; const triggerWithCorrectType = (source: Function, type: RequestType) => { requestTypeRef.value = type; source(); }; const wrapper = shallowMount( defineComponent({ setup() { const { run, refresh, error } = useRequest( () => { switch (requestTypeRef.value) { case RequestType.errorRetry: errorRetryCountRef.value += 1; break; case RequestType.run: runCountRef.value += 1; break; case RequestType.refresh: refreshCountRef.value += 1; break; } if ( requestTypeRef.value === RequestType.run || requestTypeRef.value === RequestType.refresh ) { requestTypeRef.value = RequestType.errorRetry; } return failedRequest(); }, { errorRetryCount: 5, errorRetryInterval: 500, }, ); return () => ( <div> <div id="error">{`${error.value?.message}`}</div> <button id="run" onClick={() => { run(); }} /> <button id="refresh" onClick={() => { refresh(); }} /> </div> ); }, }), ); const errorRes = () => wrapper.find('#error').text(); const run = () => wrapper.find('#run').trigger('click'); const refresh = () => wrapper.find('#refresh').trigger('click'); /* ------------------------------------- run ------------------------------------- */ expectCount(runCountRef, 1); expectCount(errorRetryCountRef, 0); expect(errorRes()).toBe('undefined'); // wait for request await waitForTime(1000); // receive a error result expect(errorRes()).not.toBe('undefined'); // wait for error retry await waitForTime(500); expectCount(runCountRef, 1); expectCount(errorRetryCountRef, 1); await waitForTime(1000); // error retry is pending await waitForTime(300); triggerWithCorrectType(run, RequestType.run); expectCount(runCountRef, 2); expectCount(errorRetryCountRef, 1); await waitForTime(1000); await waitForTime(500); expectCount(runCountRef, 2); expectCount(errorRetryCountRef, 2); /* ------------------------------------- refresh ------------------------------------- */ await waitForTime(1000); expectCount(runCountRef, 2); expectCount(errorRetryCountRef, 2); triggerWithCorrectType(refresh, RequestType.refresh); expectCount(refreshCountRef, 1); expectCount(errorRetryCountRef, 2); await waitForTime(1000); await waitForTime(500); expectCount(refreshCountRef, 1); expectCount(errorRetryCountRef, 3); await waitForTime(1000); // error retry is pending await waitForTime(300); triggerWithCorrectType(refresh, RequestType.refresh); expectCount(refreshCountRef, 2); expectCount(errorRetryCountRef, 3); // receive a error result await waitForTime(1000); // start error retry for (let index = 0; index < 100; index++) { await waitForTime(1000); await waitForTime(500); } expectCount(runCountRef, 2); expectCount(refreshCountRef, 2); // 5 times is the retry count expectCount(errorRetryCountRef, 3 + 5); }); test('pollingWhenOffline should work. case 1', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data, loading } = useRequest(() => request((count += 1)), { pollingInterval: 500, }); return () => <button>{`${loading.value || data.value}`}</button>; }, }), ); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${index + 1}`); await waitForTime(500); } // mock offline Object.defineProperty(window.navigator, 'onLine', { value: false, writable: true, }); // last request expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`1001`); await waitForTime(500); expect(wrapper.text()).toBe(`1001`); // mock online Object.defineProperty(window.navigator, 'onLine', { value: true, writable: true, }); jsdom.window.dispatchEvent(new Event('online')); await waitForTime(1); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${1001 + index + 1}`); await waitForTime(500); } }); test('pollingWhenOffline should work. case 2', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data, loading } = useRequest(() => request((count += 1)), { pollingInterval: 500, pollingWhenOffline: true, }); return () => <button>{`${loading.value || data.value}`}</button>; }, }), ); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${index + 1}`); await waitForTime(500); } // mock offline Object.defineProperty(window.navigator, 'onLine', { value: false, writable: true, }); // last request expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`1001`); await waitForTime(500); expect(wrapper.text()).toBe(`true`); // mock online Object.defineProperty(window.navigator, 'onLine', { value: true, writable: true, }); jsdom.window.dispatchEvent(new Event('online')); await waitForTime(1); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${1001 + index + 1}`); await waitForTime(500); } }); test('pollingWhenOffline should work with pollingWhenHidden', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data, loading } = useRequest(() => request((count += 1)), { pollingInterval: 500, }); return () => <button>{`${loading.value || data.value}`}</button>; }, }), ); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${index + 1}`); await waitForTime(500); } // mock offline Object.defineProperty(window.navigator, 'onLine', { value: false, writable: true, }); // last request expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`1001`); await waitForTime(500); expect(wrapper.text()).toBe(`1001`); // mock tab show Object.defineProperty(document, 'visibilityState', { value: 'visible', writable: true, }); jsdom.window.dispatchEvent(new Event('visibilitychange')); // wait 1ms make to sure event has trigger await waitForTime(1); expect(wrapper.text()).toBe(`1001`); // mock online Object.defineProperty(window.navigator, 'onLine', { value: true, writable: true, }); jsdom.window.dispatchEvent(new Event('online')); // wait 1ms to make sure event has trigger await waitForTime(1); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${1001 + index + 1}`); await waitForTime(500); } }); test('listener should unsubscribe when the component was unmounted', async () => { let count = 0; const wrapper = shallowMount( defineComponent({ setup() { const { data, loading } = useRequest(() => request((count += 1)), { pollingInterval: 500, }); return () => <button>{`${loading.value || data.value}`}</button>; }, }), ); for (let index = 0; index < 1000; index++) { expect(wrapper.text()).toBe('true'); await waitForTime(1000); expect(wrapper.text()).toBe(`${index + 1}`); await waitForTime(500); } expect(RECONNECT_LISTENER.size).toBe(1); wrapper.unmount(); expect(RECONNECT_LISTENER.size).toBe(0); }); test('global options should work', async () => { const ComponentA = defineComponent({ setup() { const { data, run } = useRequest(request); return () => <button onClick={() => run()}>{data.value}</button>; }, }); const ComponentB = defineComponent({ setup() { const { data, run } = useRequest(request); return () => <button onClick={() => run()}>{data.value}</button>; }, }); setGlobalOptions({ manual: true }); let wrapperA = shallowMount(ComponentA); let wrapperB = shallowMount(ComponentB); expect(wrapperA.find('button').text()).toBe(''); expect(wrapperB.find('button').text()).toBe(''); await waitForTime(1000); expect(wrapperA.find('button').text()).toBe(''); expect(wrapperB.find('button').text()).toBe(''); await wrapperA.find('button').trigger('click'); await wrapperB.find('button').trigger('click'); await waitForTime(1000); expect(wrapperA.find('button').text()).toBe('success'); expect(wrapperB.find('button').text()).toBe('success'); // clear global options clearGlobalOptions(); wrapperA = shallowMount(ComponentA); wrapperB = shallowMount(ComponentB); expect(wrapperA.find('button').text()).toBe(''); expect(wrapperB.find('button').text()).toBe(''); await waitForTime(1000); expect(wrapperA.find('button').text()).toBe('success'); expect(wrapperB.find('button').text()).toBe('success'); }); test('RequestConfig should work', async () => { const createComponent = (id: string, requestOptions: GlobalOptions = {}) => defineComponent({ setup() { const { loading, run } = useRequest(request, requestOptions); return () => ( <button id={id} onClick={run}> {`${loading.value}`} </button> ); }, }); const ComponentA = createComponent('A'); const ComponentB = createComponent('B'); const ComponentC = createComponent('C'); const ComponentD = createComponent('D'); const ComponentE = createComponent('E', { loadingDelay: 800 }); setGlobalOptions({ manual: true, loadingDelay: 500, }); const Wrapper = defineComponent({ setup() { return () => ( <div id="root"> <RequestConfig config={{ loadingDelay: 0 }}> <ComponentA /> </RequestConfig> <RequestConfig config={{ manual: false }}> <ComponentB /> <ComponentE /> {/* nested */} <RequestConfig config={{ manual: true, loadingDelay: 200 }}> <ComponentC /> </RequestConfig> </RequestConfig> <ComponentD /> </div> ); }, }); const wrapperA = mount(Wrapper); expect(wrapperA.find('#A').text()).toBe('false'); expect(wrapperA.find('#B').text()).toBe('false'); expect(wrapperA.find('#C').text()).toBe('false'); expect(wrapperA.find('#D').text()).toBe('false'); expect(wrapperA.find('#E').text()).toBe('false'); await wrapperA.find('#A').trigger('click'); await wrapperA.find('#C').trigger('click'); await wrapperA.find('#D').trigger('click'); expect(wrapperA.find('#A').text()).toBe('true'); expect(wrapperA.find('#B').text()).toBe('false'); expect(wrapperA.find('#C').text()).toBe('false'); expect(wrapperA.find('#D').text()).toBe('false'); expect(wrapperA.find('#E').text()).toBe('false'); await waitForTime(200); expect(wrapperA.find('#A').text()).toBe('true'); expect(wrapperA.find('#B').text()).toBe('false'); expect(wrapperA.find('#C').text()).toBe('true'); expect(wrapperA.find('#D').text()).toBe('false'); expect(wrapperA.find('#E').text()).toBe('false'); await waitForTime(300); expect(wrapperA.find('#A').text()).toBe('true'); expect(wrapperA.find('#B').text()).toBe('true'); expect(wrapperA.find('#C').text()).toBe('true'); expect(wrapperA.find('#D').text()).toBe('true'); expect(wrapperA.find('#E').text()).toBe('false'); await waitForTime(300); expect(wrapperA.find('#A').text()).toBe('true'); expect(wrapperA.find('#B').text()).toBe('true'); expect(wrapperA.find('#C').text()).toBe('true'); expect(wrapperA.find('#D').text()).toBe('true'); expect(wrapperA.find('#E').text()).toBe('true'); await waitForTime(200); expect(wrapperA.find('#A').text()).toBe('false'); expect(wrapperA.find('#B').text()).toBe('false'); expect(wrapperA.find('#C').text()).toBe('false'); expect(wrapperA.find('#D').text()).toBe('false'); expect(wrapperA.find('#E').text()).toBe('false'); wrapperA.unmount(); // clear global options clearGlobalOptions(); const wrapperB = mount(Wrapper); expect(wrapperB.find('#A').text()).toBe('true'); expect(wrapperB.find('#B').text()).toBe('true'); expect(wrapperB.find('#C').text()).toBe('false'); expect(wrapperB.find('#D').text()).toBe('true'); expect(wrapperB.find('#E').text()).toBe('false'); await wrapperB.find('#C').trigger('click'); await waitForTime(200); expect(wrapperB.find('#A').text()).toBe('true'); expect(wrapperB.find('#B').text()).toBe('true'); expect(wrapperB.find('#C').text()).toBe('true'); expect(wrapperB.find('#D').text()).toBe('true'); expect(wrapperB.find('#E').text()).toBe('false'); await waitForTime(600); expect(wrapperB.find('#A').text()).toBe('true'); expect(wrapperB.find('#B').text()).toBe('true'); expect(wrapperB.find('#C').text()).toBe('true'); expect(wrapperB.find('#D').text()).toBe('true'); expect(wrapperB.find('#E').text()).toBe('true'); await waitForTime(200); expect(wrapperB.find('#A').text()).toBe('false'); expect(wrapperB.find('#B').text()).toBe('false'); expect(wrapperB.find('#C').text()).toBe('false'); expect(wrapperB.find('#D').text()).toBe('false'); expect(wrapperB.find('#E').text()).toBe('false'); }); test('reload should work: case 1', async () => { const wrapper = shallowMount( defineComponent({ setup() { const { run, reload, reloading, data } = useRequest(request, { defaultParams: ['hello'], }); return () => ( <div> <div class="reloading">{`${reloading.value}`}</div> <button class="run" onClick={() => run('hi there')}></button> <button class="reload" onClick={() => reload()}></button> <div class="data">{data.value}</div> </div> ); }, }), ); const dataEl = wrapper.find('.data'); const runEl = wrapper.find('.run'); const reloadingEl = wrapper.find('.reloading'); const reloadEl = wrapper.find('.reload'); expect(reloadingEl.text()).toBe('false'); await waitForTime(1000); expect(reloadingEl.text()).toBe('false'); expect(dataEl.text()).toBe('hello'); await runEl.trigger('click'); expect(reloadingEl.text()).toBe('false'); await waitForTime(1000); expect(reloadingEl.text()).toBe('false'); expect(dataEl.text()).toEqual('hi there'); await reloadEl.trigger('click'); expect(reloadingEl.text()).toBe('true'); await waitForTime(1000); expect(reloadingEl.text()).toBe('false'); expect(dataEl.text()).toEqual('hello'); await runEl.trigger('click'); expect(reloadingEl.text()).toBe('false'); await waitForTime(1000); expect(reloadingEl.text()).toBe('false'); expect(dataEl.text()).toEqual('hi there'); }); test('reload should work: case 2', async () => { const users = [ { id: '1', username: 'A' }, { id: '2', username: 'B' }, { id: '3', username: 'C' }, ]; const wrapper = shallowMount( defineComponent({ setup() { const { run, queries, data, loading, reload } = useRequest(request, { manual: true, refreshOnWindowFocus: true, queryKey: id => id, }); return () => ( <div> <div id="data">{data.value}</div> <div id="loading">{loading.value.toString()}</div> <div id="reload" onClick={() => reload()} /> <ul> {users.map(item => ( <li key={item.id} id={item.username} onClick={() => run(item.id)} > {queries[item.id]?.loading ? 'loading' : queries[item.id]?.data} </li> ))} </ul> </div> ); }, }), ); const dataEl = wrapper.find('#data'); const loadingEl = wrapper.find('#loading'); const reloadEl = wrapper.find('#reload'); expect(FOCUS_LISTENER.size).toBe(1); expect(VISIBLE_LISTENER.size).toBe(2); expect(RECONNECT_LISTENER.size).toBe(1); for (let i = 0; i < users.length; i++) { const userName = users[i].username; const currentId = users[i].id; await wrapper.find(`#${userName}`).trigger('click'); expect(wrapper.find(`#${userName}`).text()).toBe('loading'); expect(dataEl.text()).toBe(''); expect(loadingEl.text()).toBe('true'); await waitForTime(1000); expect(wrapper.find(`#${userName}`).text()).toBe(currentId); expect(dataEl.text()).toBe(currentId); expect(loadingEl.text()).toBe('false'); } expect(FOCUS_LISTENER.size).toBe(4); expect(VISIBLE_LISTENER.size).toBe(8); expect(RECONNECT_LISTENER.size).toBe(4); await reloadEl.trigger('click'); for (let i = 0; i < users.length; i++) { const userName = users[i].username; expect(wrapper.find(`#${userName}`).text()).toBe(''); expect(dataEl.text()).toBe(''); } expect(FOCUS_LISTENER.size).toBe(1); expect(VISIBLE_LISTENER.size).toBe(2); expect(RECONNECT_LISTENER.size).toBe(1); for (let i = 0; i < users.length; i++) { const userName = users[i].username; const currentId = users[i].id; await wrapper.find(`#${userName}`).trigger('click'); expect(wrapper.find(`#${userName}`).text()).toBe('loading'); expect(dataEl.text()).toBe(''); expect(loadingEl.text()).toBe('true'); await waitForTime(1000); expect(wrapper.find(`#${userName}`).text()).toBe(currentId); expect(dataEl.text()).toBe(currentId); expect(loadingEl.text()).toBe('false'); } expect(FOCUS_LISTENER.size).toBe(4); expect(VISIBLE_LISTENER.size).toBe(8); expect(RECONNECT_LISTENER.size).toBe(4); }); test('onBefore and onAfter hooks can use', async () => { const onBefore = jest.fn(); const onAfter = jest.fn(); const wrapper = shallowMount( defineComponent({ setup() { const { data, run } = useRequest(request, { onBefore, onAfter, }); return () => ( <button onClick={() => run()}>{`data:${data.value}`}</button> ); }, }), ); expect(onBefore).toHaveBeenCalledTimes(1); expect(onAfter).toHaveBeenCalledTimes(0); await waitForTime(100); expect(onBefore).toHaveBeenCalledTimes(1); expect(onAfter).toHaveBeenCalledTimes(0); await waitForTime(800); expect(onBefore).toHaveBeenCalledTimes(1); expect(onAfter).toHaveBeenCalledTimes(0); await waitForTime(100); expect(onBefore).toHaveBeenCalledTimes(1); expect(onAfter).toHaveBeenCalledTimes(1); }); });
Example #24
Source File: useAsyncQuery.ts From vue-request with MIT License | 4 votes |
function useAsyncQuery<R, P extends unknown[]>(
query: Query<R, P>,
options: BaseOptions<R, P>,
): BaseResult<R, P> {
const injectedGlobalOptions = inject<GlobalOptions>(
GLOBAL_OPTIONS_PROVIDE_KEY,
{},
);
const {
cacheKey,
defaultParams = ([] as unknown) as P,
manual = false,
ready = ref(true),
refreshDeps = [],
loadingDelay = 0,
pollingWhenHidden = false,
pollingWhenOffline = false,
refreshOnWindowFocus = false,
refocusTimespan = 5000,
cacheTime = 600000,
staleTime = 0,
errorRetryCount = 0,
errorRetryInterval = 0,
queryKey,
...rest
} = {
...getGlobalOptions(),
...injectedGlobalOptions,
...options,
};
const stopPollingWhenHiddenOrOffline = ref(false);
// skip debounce when initail run
const initialAutoRunFlag = ref(false);
const updateCache = (state: State<R, P>) => {
if (!cacheKey) return;
const cacheData = getCache<R, P>(cacheKey)?.data;
const cacheQueries = cacheData?.queries;
const queryData = unRefObject(state);
const currentQueryKey =
queryKey?.(...state.params.value) ?? QUERY_DEFAULT_KEY;
setCache<R, P>(
cacheKey,
{
queries: {
...cacheQueries,
[currentQueryKey]: {
...cacheQueries?.[currentQueryKey],
...queryData,
},
},
latestQueriesKey: currentQueryKey,
},
cacheTime,
);
};
const config = {
initialAutoRunFlag,
loadingDelay,
pollingWhenHidden,
pollingWhenOffline,
stopPollingWhenHiddenOrOffline,
cacheKey,
errorRetryCount,
errorRetryInterval,
refreshOnWindowFocus,
refocusTimespan,
updateCache,
...omit(rest, ['pagination', 'listKey']),
} as Config<R, P>;
const loading = ref(false);
const data = ref<R>();
const error = ref<Error>();
const params = ref() as Ref<P>;
const queries = <Queries<R, P>>reactive({
[QUERY_DEFAULT_KEY]: reactive(createQuery(query, config)),
});
const latestQueriesKey = ref(QUERY_DEFAULT_KEY);
const latestQuery = computed(() => queries[latestQueriesKey.value] ?? {});
// sync state
watch(
latestQuery,
queryData => {
loading.value = queryData.loading;
data.value = queryData.data;
error.value = queryData.error;
params.value = queryData.params;
},
{
immediate: true,
deep: true,
},
);
// init queries from cache
if (cacheKey) {
const cache = getCache<R, P>(cacheKey);
if (cache?.data?.queries) {
Object.keys(cache.data.queries).forEach(key => {
const cacheQuery = cache.data.queries![key];
queries[key] = <UnWrapState<R, P>>reactive(
createQuery(query, config, {
loading: cacheQuery.loading,
params: cacheQuery.params,
data: cacheQuery.data,
error: cacheQuery.error,
}),
);
});
/* istanbul ignore else */
if (cache.data.latestQueriesKey) {
latestQueriesKey.value = cache.data.latestQueriesKey;
}
}
}
const tempReadyParams = ref();
const hasTriggerReady = ref(false);
const run = (...args: P) => {
if (!ready.value && !hasTriggerReady.value) {
tempReadyParams.value = args;
return resolvedPromise;
}
const newKey = queryKey?.(...args) ?? QUERY_DEFAULT_KEY;
if (!queries[newKey]) {
queries[newKey] = <UnWrapState<R, P>>reactive(createQuery(query, config));
}
latestQueriesKey.value = newKey;
return latestQuery.value.run(...args);
};
const reset = () => {
unmountQueries();
latestQueriesKey.value = QUERY_DEFAULT_KEY;
queries[QUERY_DEFAULT_KEY] = <UnWrapState<R, P>>(
reactive(createQuery(query, config))
);
};
// unmount queries
const unmountQueries = () => {
Object.keys(queries).forEach(key => {
queries[key].cancel();
queries[key].unmount();
delete queries[key];
});
};
const cancel = () => latestQuery.value.cancel();
const refresh = () => latestQuery.value.refresh();
const mutate = <Mutate<R>>((arg: R) => latestQuery.value.mutate(arg));
// initial run
if (!manual) {
initialAutoRunFlag.value = true;
// TODO: need refactor
const cache = getCache<R, P>(cacheKey!);
const cacheQueries = cache?.data.queries ?? {};
const isFresh =
cache &&
(staleTime === -1 || cache.cacheTime + staleTime > new Date().getTime());
const hasCacheQueries = Object.keys(cacheQueries).length > 0;
if (!isFresh) {
if (hasCacheQueries) {
Object.keys(queries).forEach(key => {
queries[key]?.refresh();
});
} else {
run(...defaultParams);
}
}
initialAutoRunFlag.value = false;
}
// watch ready
const stopReady = ref();
stopReady.value = watch(
ready,
val => {
hasTriggerReady.value = true;
if (val && tempReadyParams.value) {
run(...tempReadyParams.value);
// destroy current watch
stopReady.value();
}
},
{
flush: 'sync',
},
);
// watch refreshDeps
if (refreshDeps.length) {
watch(refreshDeps, () => {
!manual && latestQuery.value.refresh();
});
}
onUnmounted(() => {
unmountQueries();
});
return {
loading,
data,
error,
params,
cancel,
refresh,
mutate,
run,
reset,
queries,
};
}
Example #25
Source File: index.ts From elenext with MIT License | 4 votes |
usePopper = (props: UsePopperOptions) => {
const popperId = uniqueId('el-popper')
const { referenceRef, popperRef } = props
const timers: {
showTimer: any
hideTimer: any
} = { showTimer: undefined, hideTimer: undefined }
const state = reactive<usePopperState>({
instance: null,
popperId,
attrs: {
styles: {
popper: {
position: 'absolute',
left: '0',
top: '0',
},
arrow: {
position: 'absolute',
},
},
attributes: {},
},
})
const popperOptions = computed<PopperOptions>(() => {
return {
placement: props.placement || 'bottom-start',
strategy: 'absolute',
modifiers: [
{
name: 'updateState',
enabled: true,
phase: 'write',
fn: ({ state: popperState }: any) => {
const elements = Object.keys(popperState.elements)
state.attrs = {
styles: fromEntries(elements.map(element => [element, popperState.styles[element] || {}])),
attributes: fromEntries(elements.map(element => [element, popperState.attributes[element] || {}])),
}
},
requires: ['computeStyles'],
},
{ name: 'applyStyles', enabled: false },
{ name: 'offset', options: { offset: [0, props.offset || 0] } },
],
}
})
const clearScheduled = () => {
clearTimeout(timers.hideTimer)
clearTimeout(timers.showTimer)
}
let clickEvent: any = null
const togglePopper = (event: MouseEvent) => {
clickEvent = event
props.onTrigger(popperId)
}
const showPopper = () => {
clearScheduled()
timers.showTimer = setTimeout(() => {
props.onTrigger(popperId, true)
}, 0)
}
const hidePopper = () => {
clearScheduled()
timers.hideTimer = setTimeout(() => {
props.onTrigger(popperId, false)
}, props.hideDaly || 200)
}
const outSideClickHandler = (event: MouseEvent) => {
// outSideClick 和 togglePopper 冲突
if (event === clickEvent) {
return
}
if (popperRef.value && !popperRef.value.contains(event.target as Node)) {
if (
['hover', 'focus'].indexOf(props.trigger) !== -1 &&
referenceRef.value &&
referenceRef.value.contains(event.target as Node)
) {
return
} else {
hidePopper()
}
}
}
const eventRegOrUnReg = isReg => {
const referenceEl = referenceRef.value
const popperEl = popperRef.value
const event = isReg ? 'addEventListener' : 'removeEventListener'
if (referenceEl && popperEl) {
if (props.trigger === 'hover') {
referenceEl[event]('mouseenter', showPopper)
referenceEl[event]('mouseleave', hidePopper)
popperEl[event]('mouseenter', showPopper)
popperEl[event]('mouseleave', hidePopper)
}
if (props.trigger === 'click') {
referenceEl[event]('click', togglePopper)
// popperEl[event]('mouseenter', showPopper)
// popperEl[event]('mouseleave', hidePopper)
}
if (props.trigger === 'focus') {
referenceEl[event]('focus', showPopper)
referenceEl[event]('blur', hidePopper)
}
if (props.trigger !== 'manual') {
document[event]('click', outSideClickHandler)
}
}
}
watchEffect(() => {
if (state.instance) {
state.instance.setOptions(popperOptions.value)
}
})
watch([referenceRef, popperRef], () => {
const referenceEl = referenceRef.value
const popperEl = popperRef.value
if (referenceEl && popperEl) {
if (state.instance) {
state.instance.destroy()
}
state.instance = createPopper(referenceEl, popperEl as HTMLElement, popperOptions.value)
}
})
watchEffect(onInvalidate => {
const referenceEl = referenceRef.value
const popperEl = popperRef.value
onInvalidate(() => {
eventRegOrUnReg(false)
})
if (referenceEl && popperEl) {
eventRegOrUnReg(true)
}
})
onUnmounted(() => {
if (state.instance) {
state.instance.destroy()
}
})
return state
}
Example #26
Source File: document.ts From quantum-sheet with GNU General Public License v3.0 | 4 votes |
/**
* Create a document
* @param elementTypes Element types in the document
*/
export function useDocument<TElements extends QuantumDocumentElementTypes<readonly QuantumElementType[]>>(
elementTypes: TElements
): UseQuantumDocument<TElements> {
const options = reactive<DocumentOptions>({
gridCellSize: readonly(new Vector2(20, 20)),
paperStyle: 'standard',
paperSize: 'A4',
})
const elementRemoveCallbacks = new Map<string, () => void>()
const elementList = useElementList()
const elementSelection = useElementSelection()
const elementFocus = useElementFocus()
// TODO: Prevent this from being moved
const rootScope = createElement(ScopeElementType.typeName, {
position: Vector2.zero,
size: Vector2.zero,
})
function addElement<T extends QuantumElement>(element: T): T {
// TODO: I think we can use the effectScope API here https://github.com/vuejs/rfcs/blob/master/active-rfcs/0041-reactivity-effect-scope.md
// (Replacing the stopHandles)
let stopHandles = [elementList.watchElement(element), elementSelection.watchElement(element), elementFocus.watchElement(element)]
elementRemoveCallbacks.set(element.id, () => {
stopHandles.forEach((stopHandle) => stopHandle())
})
return element
}
function createElement<T extends keyof TElements>(typeName: T, options: QuantumElementCreationOptions): GetQuantumElement<TElements[T]> {
let elementType = elementTypes[typeName]
if (!elementType) throw new Error(`Unknown element type ${typeName}`)
const element = new elementType.elementType(options)
//elementType.useElement(useQuantumElement('' + typeName, options)) as ReturnType<TElements[T]['useElement']>
addElement(element)
return element as any
}
function deleteElement(element: QuantumElement) {
let removeCallback = elementRemoveCallbacks.get(element.id)
if (removeCallback) {
removeCallback()
elementRemoveCallbacks.delete(element.id)
}
}
function getElementById<T extends keyof TElements>(id: string, typeName?: T): GetQuantumElement<TElements[T]> | undefined {
let element = elementList.elements.find((e: QuantumElement) => e.id == id)
if (element && typeName && element.typeName != typeName) {
throw new Error(`Wrong type, passed ${typeName} but element has ${element.typeName}`)
}
// Yeah, Typescript really does dislike this XD
return element as any
}
function getElementsByType<T extends keyof TElements>(typeName: T): GetQuantumElement<TElements[T]>[] | undefined {
let elements = elementList.elements.filter((e: QuantumElement) => e.typeName == typeName)
// Yeah, Typescript really does dislike this XD
return elements as any[]
}
function serializeDocument() {
let serializedData: SerializedDocument = {
version: pkg.version,
options: serializeOptions(options),
elements: [],
}
elementList.elements.forEach((element: QuantumElement) => {
let elementType = elementTypes[element.typeName]
serializedData.elements.push(elementType.serializeElement(element))
})
return serializedData
}
function deserializeDocument(serializedData: SerializedDocument) {
if (serializedData?.options) {
const deserializedOptions = deserializeOptions(serializedData?.options)
options.gridCellSize = deserializedOptions.gridCellSize ?? options.gridCellSize
options.paperStyle = deserializedOptions.paperStyle ?? options.paperStyle
options.paperSize = deserializedOptions.paperSize ?? options.paperSize
}
serializedData?.elements?.forEach((elementData: JsonType) => {
let elementType = elementTypes[(elementData as any).typeName]
if (!elementType) {
console.warn('Element is missing its type', elementData)
}
const { element, onAddedCallback } = elementType.deserializeElement(elementData)
addElement(element)
onAddedCallback()
})
}
function moveElements(elements: QuantumElement[], delta: Vector2, limit?: Vector2) {
// TODO: dont let it move outside sheet (thus no longer needing 'interact.modifiers.restrict'?)
let limited = false
elements.forEach((element: QuantumElement) => {
let newPos = element?.position.value.add(delta)
if (limit) {
if (
newPos.x < 0 ||
newPos.y < 0 ||
limit.subtract(newPos.add(element.size.value)).x < 0 ||
limit.subtract(newPos.add(element.size.value)).y < 0
) {
limited = true
}
}
})
if (limited) return
elements.forEach((element: QuantumElement) => {
let newPos = element?.position.value.add(delta)
if (newPos) element?.setPosition(newPos)
})
}
function moveSelectedElements(delta: Vector2, limit?: Vector2) {
moveElements(elementSelection.selectedElements, delta, limit)
}
return {
options,
elementTypes: elementTypes,
elements: elementList.elements,
createElement,
deleteElement,
getElementAt: elementList.getElementAt,
getElementById,
getSelection: () => [...elementSelection.selectedElements],
setSelection: elementSelection.setSelection,
setFocus: elementFocus.setFocus,
moveElements,
moveSelectedElements,
serializeDocument,
deserializeDocument,
}
}
Example #27
Source File: auto.spec.ts From vue3-treeview with MIT License | 4 votes |
describe("test auto checkbox", () => {
let node = null;
let nodes = null;
let storeProps = null;
let mode = null;
beforeEach(() => {
node = ref({
children: ["c1", "c2"],
state: {
checked: false,
indeterminate: false
}
});
nodes = ref({
c1: {
text: "c1",
state: {
checked: false,
indeterminate: false
}
},
c2: {
text: "c2",
state: {
checked: false,
indeterminate: false
}
}
});
storeProps = reactive({
nodes
});
mode = auto(node, nodes);
});
it("Expect not to be checked", () => {
expect(mode.checked.value).toBeFalsy();
});
it("Expect not to be indeterminate", () => {
expect(mode.indeterminate.value).toBeFalsy();
});
it("Expect none to be checked", () => {
expect(mode.noneChecked.value).toBeTruthy();
});
it("Expect not all to be checked" , () => {
expect(mode.allChecked.value).toBeFalsy();
});
it("Expect to no have some indeterminate", () => {
expect(mode.someIndeterminate.value).toBeFalsy();
});
it("Expect not some checked" , () => {
expect(mode.someChecked.value).toBeFalsy();
});
it("Expect children to have state", () => {
expect(nodes.value.c1.state).toBeDefined();
expect(nodes.value.c2.state).toBeDefined();
});
it("Expect to check node", () => {
mode.click();
expect(node.value.state.checked).toBeTruthy();
expect(node.value.state.indeterminate).toBeFalsy();
});
it("Expect to rebuild", () => {
node.value.state.checked = true;
mode.rebuild();
expect(nodes.value.c1.state.checked).toBeTruthy();
expect(nodes.value.c2.state.checked).toBeTruthy();
});
it("Expect not to update state", () => {
node.value.children = [];
mode.updateState();
expect(nodes.value.c1.state.checked).toBeFalsy();
expect(nodes.value.c2.state.checked).toBeFalsy();
});
it("Expect to reset node checked on update state", () => {
node.value.state.checked = true;
node.value.state.indeterminate = true;
mode.updateState();
expect(node.value.state.checked).toBeFalsy();
expect(node.value.state.indeterminate).toBeFalsy();
});
it("Expect node to be indeterminate after update", () => {
nodes.value.c1.state.indeterminate = true;
mode.updateState();
expect(node.value.state.checked).toBeFalsy();
expect(node.value.state.indeterminate).toBeTruthy();
});
it("Expect to update nothing", () => {
node.value.children = null;
mode.updateState();
expect(node.value.state.checked).toBeFalsy();
expect(node.value.state.indeterminate).toBeFalsy();
});
});
Example #28
Source File: useCommon.spec.ts From vue3-treeview with MIT License | 4 votes |
describe("test useCommon", () => {
let props = null;
let useTest = null;
const config = {
roots: ["id1"],
disabled: false,
editing: null
};
const storeProps = reactive({
nodes: {},
config
});
const v = require("vue");
let state = null;
v.inject = jest.fn((s) => {
return s === "emitter" ? jest.fn() : {
config: ref(config)
}
});
beforeEach(() => {
props = reactive({
node: ref({
id: "test"
})
});
const id = createState(storeProps);
state = states.get(id);
useTest = useCommon(props);
});
it("Expect to have state", () => {
expect(props.node.state).toBeDefined();
});
it("Expect to have node", () => {
expect(useTest.hasNode.value).toBeTruthy();
});
it("Expect to have config", () => {
expect(useTest.hasConfig.value).toBeTruthy();
});
it("Expect to have state", () => {
expect(useTest.hasState.value).toBeTruthy();
});
it("Expect not to be disabled", () => {
expect(useTest.disabled.value).toBeFalsy();
});
it("Expect not to be editable", () => {
expect(useTest.editable.value).toBeFalsy();
});
it("Expect not to be edited", () => {
expect(useTest.editing.value).toBeFalsy();
});
it("Expect to be editable", () => {
state.config.value.editable = true;
props.node.state.editable = true;
expect(useTest.editable.value).toBeTruthy();
});
it("Expect to be editing", () => {
state.config.value.editable = true;
state.config.value.editing = "test";
props.node.state.editable = true;
expect(useTest.editable.value).toBeTruthy();
});
it("Expect to blur", () => {
state.config.value.editing = "tata";
const e = {
currentTarget: document.createElement("div"),
relatedTarget: document.createElement("div"),
type: "blur"
};
useTest.blur(e);
expect(state.config.value.editing).toBeNull();
});
});
Example #29
Source File: useDragAndDrop.spec.ts From vue3-treeview with MIT License | 4 votes |
describe("test use Drag and Drop", () => {
let fakeCmn = null;
let useTest = null;
let wrapper = null;
let props = null;
let node = null;
let nodes = null;
let node2 = null;
let c1 = null;
let c2 = null;
let c3 = null;
let config = null;
let storeProps = null;
let fakeDragged = null;
let fakeTarget = null;
let fakeContext = null;
let state = null;
beforeEach(() => {
node = {
text: "id1",
id: "id1",
children: ["id11", "id12"],
state: {}
};
node2 = {
text: "id2",
id: "id2",
children: ["id21"],
state:{}
};
c1 = {
id: "id11",
text: "id11",
parent: "id1",
state: {}
};
c2 = {
id: "id12",
text: "id12",
parent: "id1",
state: {}
};
c3 = {
id: "id21",
text: "id21",
parent: "id2",
state: {}
};
config = ref({
roots: ["id1", "id2"]
});
nodes = {
id1: node,
id11: c1,
id12: c2,
id2: node2,
id21: c3
};
storeProps = reactive({
nodes,
config
});
const id = createState(storeProps);
state = states.get(id);
wrapper = ref(document.createElement("div"));
fakeCmn = {
node: ref(node),
config,
wrapper,
disabled: ref(false),
root: {
emit: jest.fn()
},
state
};
fakeDragged = {
element: null,
node: null,
parentId: null,
wrapper: null
};
fakeTarget = {
element: null,
node: 'id1',
parentId: null,
wrapper: wrapper.value
};
fakeContext = {
dataTransfer: null,
dragged: fakeDragged,
target: fakeTarget,
evt: undefined,
external: false
};
props = {
parentId: ref(null)
};
useTest = useDragAndDrop(fakeCmn, props);
});
it("Expect not to be draggable", () => {
expect(useTest.draggable.value).toBeFalsy();
});
it("Expect not to be droppable", () => {
expect(useTest.droppable.value).toBeFalsy();
});
it("Expect element to be null", () => {
expect(useTest.element.value).toBeNull();
});
it("Expect to have have basic class", () => {
expect(useTest.dragClass.value).toMatchObject([
null,
null,
null,
null,
null,
]);
});
it("Expect to start drag", () => {
config.value.dragAndDrop = ref(true);
const spy = jest.spyOn(fakeCmn.root, "emit");
fakeCmn.node.value.state.draggable = true;
useTest.dragstart();
fakeDragged.node = node;
fakeDragged.wrapper = wrapper.value;
expect(state.dragged.value).toMatchObject(fakeDragged);
expect(spy).toBeCalledWith(dragEvents.start, fakeContext);
});
it("Expect to emit event on drag end when nothing started", () => {
config.value.dragAndDrop = ref(true);
const spy = jest.spyOn(fakeCmn.root, "emit");
useTest.dragend();
expect(spy).toBeCalledWith(dragEvents.end, fakeContext);
});
it("Expect to emit on drag enter", () => {
const spy = jest.spyOn(fakeCmn.root, "emit");
useTest.dragenter();
expect(spy).toBeCalledWith(dragEvents.enter, fakeContext);
});
it("Expect to emit on drag leave", () => {
const spy = jest.spyOn(fakeCmn.root, "emit");
useTest.dragleave();
expect(spy).toBeCalledWith(dragEvents.leave, fakeContext);
});
it("Expect to be same node", () => {
state.dragged.value = fakeDragged;
fakeDragged.node = node;
useTest.dragover();
});
it("Expect to drag node 2 in node 1", () => {
node2.state.draggable = true;
state.dragged.value = fakeDragged;
fakeDragged.node = node2;
wrapper.value.getBoundingClientRect = jest.fn(() => {
return {
bottom: 0,
height: 40,
left: 0,
right: 0,
top: 0,
width: 0
};
});
useTest.dragover({ pageY: 20 });
expect(useTest.pos.value).toBe(DragPosition.in);
});
it("Expect to drag node 2 over node 1", () => {
node2.state.draggable = true;
state.dragged.value = fakeDragged;
fakeDragged.node = node2;
wrapper.value.getBoundingClientRect = jest.fn(() => {
return {
bottom: 0,
height: 40,
left: 0,
right: 0,
top: 0,
width: 0
};
});
useTest.dragover({ pageY: 1 });
expect(useTest.pos.value).toBe(DragPosition.over);
});
it("Expect to drag child node 3 under node 1", () => {
c3.state.draggable = true;
state.dragged.value = fakeDragged;
fakeDragged.node = c3;
wrapper.value.getBoundingClientRect = jest.fn(() => {
return {
bottom: 0,
height: 40,
left: 0,
right: 0,
top: 0,
width: 0
};
});
useTest.dragover({ pageY: 35 });
expect(useTest.pos.value).toBe(DragPosition.under);
});
it("Expect to insert child node 2 over node 1", () => {
c3.state.draggable = true;
state.dragged.value = fakeDragged;
fakeDragged.parentId = "id2";
fakeDragged.node = c3;
props.parentId.value = null;
useTest.pos.value = DragPosition.over;
useTest.drop();
expect(config.value.roots).toMatchObject([
"id21", "id1", "id2"
])
expect(node2.children).toMatchObject([]);
});
it("Expect to insert child node 2 under node 1", () => {
c3.state.draggable = true;
state.dragged.value = fakeDragged;
fakeDragged.parentId = "id2";
fakeDragged.node = c3;
props.parentId.value = null;
useTest.pos.value = DragPosition.under;
useTest.drop();
expect(config.value.roots).toMatchObject([
"id1", "id21", "id2"
])
expect(node2.children).toMatchObject([]);
});
it("Expect to insert child node 2 in node 1", () => {
c3.state.draggable = true;
state.dragged.value = fakeDragged;
fakeDragged.parentId = "id2";
fakeDragged.node = c3;
config.value.dragAndDrop = true;
props.parentId.value = null;
useTest.pos.value = DragPosition.in;
useTest.drop();
expect(node.children).toMatchObject([
"id21", "id11", "id12"
])
expect(node2.children).toMatchObject([]);
});
});