]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(keep-alive): ensure include/exclude regexp work with global flag (#11595)
authorbtea <2356281422@qq.com>
Tue, 13 Aug 2024 14:07:35 +0000 (22:07 +0800)
committerGitHub <noreply@github.com>
Tue, 13 Aug 2024 14:07:35 +0000 (22:07 +0800)
packages/runtime-core/__tests__/components/KeepAlive.spec.ts
packages/runtime-core/src/components/KeepAlive.ts

index 6d3f6a9b8b67f267cb8660a9b08b7ab7c7afd740..0b761972c00ffe5445e3342577c021fe8df62e34 100644 (file)
@@ -32,6 +32,7 @@ const timeout = (n: number = 0) => new Promise(r => setTimeout(r, n))
 describe('KeepAlive', () => {
   let one: ComponentOptions
   let two: ComponentOptions
+  let oneTest: ComponentOptions
   let views: Record<string, ComponentOptions>
   let root: TestElement
 
@@ -49,6 +50,18 @@ describe('KeepAlive', () => {
       deactivated: vi.fn(),
       unmounted: vi.fn(),
     }
+    oneTest = {
+      name: 'oneTest',
+      data: () => ({ msg: 'oneTest' }),
+      render(this: any) {
+        return h('div', this.msg)
+      },
+      created: vi.fn(),
+      mounted: vi.fn(),
+      activated: vi.fn(),
+      deactivated: vi.fn(),
+      unmounted: vi.fn(),
+    }
     two = {
       name: 'two',
       data: () => ({ msg: 'two' }),
@@ -63,6 +76,7 @@ describe('KeepAlive', () => {
     }
     views = {
       one,
+      oneTest,
       two,
     }
   })
@@ -369,6 +383,128 @@ describe('KeepAlive', () => {
     assertHookCalls(two, [2, 2, 0, 0, 2])
   }
 
+  async function assertNameMatchWithFlag(props: KeepAliveProps) {
+    const outerRef = ref(true)
+    const viewRef = ref('one')
+    const App = {
+      render() {
+        return outerRef.value
+          ? h(KeepAlive, props, () => h(views[viewRef.value]))
+          : null
+      },
+    }
+    render(h(App), root)
+
+    expect(serializeInner(root)).toBe(`<div>one</div>`)
+    assertHookCalls(one, [1, 1, 1, 0, 0])
+    assertHookCalls(oneTest, [0, 0, 0, 0, 0])
+    assertHookCalls(two, [0, 0, 0, 0, 0])
+
+    viewRef.value = 'oneTest'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>oneTest</div>`)
+    assertHookCalls(one, [1, 1, 1, 1, 0])
+    assertHookCalls(oneTest, [1, 1, 1, 0, 0])
+    assertHookCalls(two, [0, 0, 0, 0, 0])
+
+    viewRef.value = 'two'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>two</div>`)
+    assertHookCalls(one, [1, 1, 1, 1, 0])
+    assertHookCalls(oneTest, [1, 1, 1, 1, 0])
+    assertHookCalls(two, [1, 1, 0, 0, 0])
+
+    viewRef.value = 'one'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>one</div>`)
+    assertHookCalls(one, [1, 1, 2, 1, 0])
+    assertHookCalls(oneTest, [1, 1, 1, 1, 0])
+    assertHookCalls(two, [1, 1, 0, 0, 1])
+
+    viewRef.value = 'oneTest'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>oneTest</div>`)
+    assertHookCalls(one, [1, 1, 2, 2, 0])
+    assertHookCalls(oneTest, [1, 1, 2, 1, 0])
+    assertHookCalls(two, [1, 1, 0, 0, 1])
+
+    viewRef.value = 'two'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>two</div>`)
+    assertHookCalls(one, [1, 1, 2, 2, 0])
+    assertHookCalls(oneTest, [1, 1, 2, 2, 0])
+    assertHookCalls(two, [2, 2, 0, 0, 1])
+
+    // teardown
+    outerRef.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<!---->`)
+    assertHookCalls(one, [1, 1, 2, 2, 1])
+    assertHookCalls(oneTest, [1, 1, 2, 2, 1])
+    assertHookCalls(two, [2, 2, 0, 0, 2])
+  }
+
+  async function assertNameMatchWithFlagExclude(props: KeepAliveProps) {
+    const outerRef = ref(true)
+    const viewRef = ref('one')
+    const App = {
+      render() {
+        return outerRef.value
+          ? h(KeepAlive, props, () => h(views[viewRef.value]))
+          : null
+      },
+    }
+    render(h(App), root)
+
+    expect(serializeInner(root)).toBe(`<div>one</div>`)
+    assertHookCalls(one, [1, 1, 0, 0, 0])
+    assertHookCalls(oneTest, [0, 0, 0, 0, 0])
+    assertHookCalls(two, [0, 0, 0, 0, 0])
+
+    viewRef.value = 'oneTest'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>oneTest</div>`)
+    assertHookCalls(one, [1, 1, 0, 0, 1])
+    assertHookCalls(oneTest, [1, 1, 0, 0, 0])
+    assertHookCalls(two, [0, 0, 0, 0, 0])
+
+    viewRef.value = 'two'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>two</div>`)
+    assertHookCalls(one, [1, 1, 0, 0, 1])
+    assertHookCalls(oneTest, [1, 1, 0, 0, 1])
+    assertHookCalls(two, [1, 1, 1, 0, 0])
+
+    viewRef.value = 'one'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>one</div>`)
+    assertHookCalls(one, [2, 2, 0, 0, 1])
+    assertHookCalls(oneTest, [1, 1, 0, 0, 1])
+    assertHookCalls(two, [1, 1, 1, 1, 0])
+
+    viewRef.value = 'oneTest'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>oneTest</div>`)
+    assertHookCalls(one, [2, 2, 0, 0, 2])
+    assertHookCalls(oneTest, [2, 2, 0, 0, 1])
+    assertHookCalls(two, [1, 1, 1, 1, 0])
+
+    viewRef.value = 'two'
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<div>two</div>`)
+    assertHookCalls(one, [2, 2, 0, 0, 2])
+    assertHookCalls(oneTest, [2, 2, 0, 0, 2])
+    assertHookCalls(two, [1, 1, 2, 1, 0])
+
+    // teardown
+    outerRef.value = false
+    await nextTick()
+    expect(serializeInner(root)).toBe(`<!---->`)
+    assertHookCalls(one, [2, 2, 0, 0, 2])
+    assertHookCalls(oneTest, [2, 2, 0, 0, 2])
+    assertHookCalls(two, [1, 1, 2, 2, 1])
+  }
+
   describe('props', () => {
     test('include (string)', async () => {
       await assertNameMatch({ include: 'one' })
@@ -378,6 +514,10 @@ describe('KeepAlive', () => {
       await assertNameMatch({ include: /^one$/ })
     })
 
+    test('include (regex with g flag)', async () => {
+      await assertNameMatchWithFlag({ include: /one/g })
+    })
+
     test('include (array)', async () => {
       await assertNameMatch({ include: ['one'] })
     })
@@ -390,6 +530,10 @@ describe('KeepAlive', () => {
       await assertNameMatch({ exclude: /^two$/ })
     })
 
+    test('exclude (regex with a flag)', async () => {
+      await assertNameMatchWithFlagExclude({ exclude: /one/g })
+    })
+
     test('exclude (array)', async () => {
       await assertNameMatch({ exclude: ['two'] })
     })
index e9c8cce58df2d704bc7bff8450762af25f789e37..11be59ffcaf76d9372e44f59e4dfe7fb2055bdef 100644 (file)
@@ -382,6 +382,7 @@ function matches(pattern: MatchPattern, name: string): boolean {
   } else if (isString(pattern)) {
     return pattern.split(',').includes(name)
   } else if (isRegExp(pattern)) {
+    pattern.lastIndex = 0
     return pattern.test(name)
   }
   /* istanbul ignore next */