Skip to content

Commit

Permalink
refactor functionality to composables
Browse files Browse the repository at this point in the history
  • Loading branch information
CachedaCodes committed Jan 17, 2024
1 parent 6c9af54 commit 8c95efc
Show file tree
Hide file tree
Showing 5 changed files with 402 additions and 76 deletions.
111 changes: 50 additions & 61 deletions packages/x-components/src/components/__tests__/display-emitter.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import { mount, Wrapper } from '@vue/test-utils';
import Vue, { ref } from 'vue';
import { Component } from 'vue-property-decorator';
import Vue, { ref, nextTick, Ref } from 'vue';
import { TaggingRequest } from '@empathyco/x-types';
import { useElementVisibility } from '@vueuse/core';
import { useEmitDisplayEvent } from '../../composables';
import DisplayEmitter from '../display-emitter.vue';
import { getDataTestSelector } from '../../__tests__/utils';

@Component({
template: `
<button data-test="child" />
`
})
class Child extends Vue {}
jest.mock('../../composables', () => ({
useEmitDisplayEvent: jest.fn()
}));

let emitDisplayEventElementSpy: Ref<Vue | null> = ref(null);
let emitDisplayEventPayloadSpy: TaggingRequest = { url: '', params: {} };
const unwatchDisplaySpy = jest.fn();
const refElementVisibility = ref(false);
(useEmitDisplayEvent as jest.Mock).mockImplementation(({ element, taggingRequest }) => {
// jest doesn't handle well evaluation of dynamic references with `toHaveBeenCalledWith`
// so we need a spy
emitDisplayEventElementSpy = element;
emitDisplayEventPayloadSpy = taggingRequest;

/* eslint-disable */
// @ts-ignore
useElementVisibility = jest.fn().mockReturnValue(refElementVisibility);
/* eslint-enable */
return {
isElementVisible: refElementVisibility,
unwatchDisplay: unwatchDisplaySpy
};
});

/**
* Renders the {@link DisplayEmitter} component, exposing a basic API for testing.
Expand All @@ -29,43 +35,26 @@ useElementVisibility = jest.fn().mockReturnValue(refElementVisibility);
function renderDisplayEmitter(
{ payload }: RenderDisplayEmitterOptions = { payload: { url: '', params: {} } }
): RenderDisplayEmitterAPI {
const emitSpy = jest.fn();

const wrapper = mount(
{
components: {
DisplayEmitter,
Child
DisplayEmitter
},
template: `
<DisplayEmitter :payload="payload">
<Child v-show="showChild" />
<div data-test="child" />
</DisplayEmitter>`,
props: ['payload', 'showChild']
props: ['payload']
},
{
propsData: {
payload,
showChild: false
},
mocks: {
$x: {
emit: emitSpy
}
payload
}
}
);

const toggleChildVisibility = async (): Promise<void> => {
refElementVisibility.value = !refElementVisibility.value;
await wrapper.vm.$nextTick();
};

return {
wrapper,
child: wrapper.findComponent<Child>(Child),
emitSpy,
toggleChildVisibility
wrapper
};
}

Expand All @@ -75,37 +64,43 @@ describe('testing DisplayEmitter component', () => {
});

it('renders everything passed to its default slot', () => {
const { child } = renderDisplayEmitter();
const { wrapper } = renderDisplayEmitter();

expect(child.exists()).toBe(true);
expect(wrapper.find(getDataTestSelector('child')).exists()).toBe(true);
});

// eslint-disable-next-line max-len
it('emits TrackableElementDisplayed with provided payload when the child is visible', async () => {
const payload = { url: 'test-url', params: { test: 'param' } };
const { emitSpy, toggleChildVisibility } = renderDisplayEmitter({
payload
});
it('uses `useEmitDisplayEvent` underneath', () => {
renderDisplayEmitter();

expect(useEmitDisplayEvent).toHaveBeenCalled();
});

await toggleChildVisibility();
it('provides `useEmitDisplayEvent` with the element in the slot to watch', async () => {
renderDisplayEmitter();

expect(emitSpy).toHaveBeenCalledTimes(1);
expect(emitSpy).toHaveBeenCalledWith('TrackableElementDisplayed', payload);
await nextTick();

expect(emitDisplayEventElementSpy.value).not.toBe(null);
expect(emitDisplayEventElementSpy.value?.$el.getAttribute('data-test')).toBe('child');
});

// eslint-disable-next-line max-len
it('emits TrackableElementDisplayed when the child is visible only for the first time', async () => {
const { emitSpy, toggleChildVisibility } = renderDisplayEmitter();

await toggleChildVisibility();
it('provides `useEmitDisplayEvent` with the payload to emit with the display event', () => {
const payload = { url: 'test-url', params: { test: 'param' } };
renderDisplayEmitter({
payload
});

expect(emitSpy).toHaveBeenCalledTimes(1);
expect(emitSpy).toHaveBeenCalledWith('TrackableElementDisplayed', expect.anything());
expect(useEmitDisplayEvent).toHaveBeenCalled();
expect(emitDisplayEventPayloadSpy).toBe(payload);
});

await toggleChildVisibility();
await toggleChildVisibility();
it('removes the watcher on unmount', async () => {
const { wrapper } = renderDisplayEmitter();

expect(emitSpy).toHaveBeenCalledTimes(1);
wrapper.destroy();
await nextTick();
expect(unwatchDisplaySpy).toHaveBeenCalled();
});
});

Expand All @@ -117,10 +112,4 @@ interface RenderDisplayEmitterOptions {
interface RenderDisplayEmitterAPI {
/** The wrapper testing component instance. */
wrapper: Wrapper<Vue>;
/** The wrapper for the child component inside the display emitter. */
child: Wrapper<Child>;
/** The emit spy. */
emitSpy: jest.Mock;
/** Toggle child visibility. */
toggleChildVisibility: () => Promise<void>;
}
23 changes: 8 additions & 15 deletions packages/x-components/src/components/display-emitter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
</template>

<script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue';
import { useElementVisibility } from '@vueuse/core';
import { defineComponent, onUnmounted, PropType, Ref, ref } from 'vue';
import { MaybeElement } from '@vueuse/core';
import { TaggingRequest } from '@empathyco/x-types';
import { use$x } from '../composables';
import { useEmitDisplayEvent } from '../composables';
import { NoElement } from './no-element';
/**
Expand All @@ -31,20 +31,13 @@
},
setup(props) {
const root = ref(null);
const isElementVisible = useElementVisibility(root);
const $x = use$x();
/**
* Emit the display event when the element is visible and stop watching the visibility of the
* element.
*/
const unwatchDisplay = watch(isElementVisible, newValue => {
if (newValue) {
$x.emit('TrackableElementDisplayed', props.payload);
unwatchDisplay();
}
const { unwatchDisplay } = useEmitDisplayEvent({
element: root as Ref<MaybeElement>,
taggingRequest: props.payload
});
onUnmounted(unwatchDisplay);
return {
root
};
Expand Down
Loading

0 comments on commit 8c95efc

Please sign in to comment.