diff --git a/src/runtime/test/render-vdom.spec.tsx b/src/runtime/test/render-vdom.spec.tsx
index c31262d0aea..00e939e5a27 100644
--- a/src/runtime/test/render-vdom.spec.tsx
+++ b/src/runtime/test/render-vdom.spec.tsx
@@ -1097,5 +1097,100 @@ describe('render-vdom', () => {
await waitForChanges();
expect(rootInstance.counter).toEqual(2);
});
+
+ it('should not call ref cb w/ null when children are reordered', async () => {
+ // this test is a regression test ensuring that the algorithm for matching
+ // up children across rerenders works correctly when a basic transposition is
+ // done (the elements at the ends of the children swap places).
+ @Component({ tag: 'cmp-a' })
+ class CmpA {
+ divRef: HTMLElement;
+ @Prop() state = true;
+
+ renderA() {
+ return (
+
(this.divRef = el)}>
+ A
+
+ );
+ }
+
+ renderB() {
+ return B
;
+ }
+
+ render() {
+ return this.state
+ ? [this.renderB(), middle
, this.renderA()]
+ : [this.renderA(), middle
, this.renderB()];
+ }
+ }
+
+ const { root, rootInstance, waitForChanges } = await newSpecPage({
+ components: [CmpA],
+ html: ``,
+ });
+ // ref should be set correctly after the first render
+ expect(rootInstance.divRef).toEqual(root.querySelector('.a'));
+ root.state = false;
+ await waitForChanges();
+ // We've changed the state and forced a re-render. This tests one of the
+ // ways in which children can be re-ordered that the `updateChildren` algo
+ // can handle without having `key` attrs set.
+ expect(rootInstance.divRef).toEqual(root.querySelector('.a'));
+ });
+
+ it('should not call ref cb w/ null when children w/ keys are reordered', async () => {
+ // this test is a regression test ensuring that the algorithm for matching
+ // up children across rerenders works correctly in a situation in which it
+ // needs to use the `key` attribute to disambiguate them. At present, if the
+ // `key` attribute is _not_ present in this case then this test will fail
+ // because without the `key` Stencil's child-identity heuristic falls over.
+ @Component({ tag: 'cmp-a' })
+ class CmpA {
+ divRef: HTMLElement;
+ @Prop() state = true;
+
+ renderA() {
+ return (
+ (this.divRef = el)}>
+ A
+
+ );
+ }
+
+ renderB() {
+ return B
;
+ }
+
+ render() {
+ return this.state ? [this.renderB(), this.renderA()] : [this.renderA()];
+ }
+ }
+
+ const { root, rootInstance, waitForChanges } = await newSpecPage({
+ components: [CmpA],
+ html: ``,
+ });
+
+ // ref should be set correctly after the first render
+ expect(rootInstance.divRef).toEqual(root.querySelector('.a'));
+ root.state = false;
+ await waitForChanges();
+ // We've changed the state and forced a re-render where the algorithm for
+ // reconciling children will have to use the `key` attribute to find the
+ // equivalent VNode on the re-render. So if that is all working correctly
+ // then the value of our `divRef` property should be set correctly after
+ // the rerender.
+ //
+ // The reordering that is conditionally done in the `render` method of the
+ // test component above is specifically the type of edge case that the
+ // parts of the `updateChildren` algorithm which _don't_ use the `key` attr
+ // have trouble with.
+ //
+ // This is essentially a regression test for the issue described in
+ // https://github.com/ionic-team/stencil/issues/3253
+ expect(rootInstance.divRef).toEqual(root.querySelector('.a'));
+ });
});
});