diff --git a/src/shared/merge.js b/src/shared/merge.js index 4a64709..ced8de3 100644 --- a/src/shared/merge.js +++ b/src/shared/merge.js @@ -25,8 +25,8 @@ export function arrayMerge({ component, tagIDKeyName, metaTemplateKeyName, conte } // when sourceItem explictly defines contentKeyName or innerHTML as undefined, its - // an indication that we need to skip the default behaviour - // So we keep the targetItem and ignore/remove the sourceItem + // an indication that we need to skip the default behaviour or child has preference over parent + // which means we keep the targetItem and ignore/remove the sourceItem if ((sourceItem.hasOwnProperty(contentKeyName) && sourceItem[contentKeyName] === undefined) || (sourceItem.hasOwnProperty('innerHTML') && sourceItem.innerHTML === undefined)) { destination.push(targetItem) diff --git a/test/components.test.js b/test/components.test.js index 21d4e95..5742836 100644 --- a/test/components.test.js +++ b/test/components.test.js @@ -109,22 +109,23 @@ describe('client', () => { }) test('changed function is called', async () => { - const parentComponent = new Vue({ render: h => h('div') }) - const wrapper = mount(Changed, { localVue: Vue, parentComponent }) - - await vmTick(wrapper.vm) - expect(wrapper.vm.$root._vueMeta.initialized).toBe(true) - let context const changed = jest.fn(function () { context = this }) - wrapper.setData({ changed, childVisible: true }) + + const wrapper = mount(Changed, { localVue: Vue, propsData: { changed } }) + + await vmTick(wrapper.vm) + expect(wrapper.vm.$root._vueMeta.initialized).toBe(true) + // TODO: does changed need to run on initialization? + expect(changed).toHaveBeenCalledTimes(1) + + wrapper.setData({ childVisible: true }) jest.runAllTimers() - expect(changed).toHaveBeenCalledTimes(1) - // TODO: this isnt what the docs say - expect(context._uid).not.toBe(wrapper.vm._uid) + expect(changed).toHaveBeenCalledTimes(2) + expect(context._uid).toBe(wrapper.vm._uid) }) test('afterNavigation function is called', () => { @@ -136,15 +137,19 @@ describe('client', () => { }) const guards = {} - Vue.prototype.$router = { - beforeEach(fn) { - guards.before = fn - }, - afterEach(fn) { - guards.after = fn + const wrapper = mount(component, { + localVue: Vue, + mocks: { + $router: { + beforeEach(fn) { + guards.before = fn + }, + afterEach(fn) { + guards.after = fn + } + } } - } - const wrapper = mount(component, { localVue: Vue }) + }) expect(guards.before).toBeDefined() expect(guards.after).toBeDefined() diff --git a/test/fixtures/changed.vue b/test/fixtures/changed.vue index 20a6de5..4d2c02d 100644 --- a/test/fixtures/changed.vue +++ b/test/fixtures/changed.vue @@ -11,16 +11,24 @@ export default { components: { HelloWorld }, + props: { + changed: { + type: Function + } + }, metaInfo() { return { - changed: this.changed + changed: this._changed } }, data() { return { childVisible: false, - changed: () => {} + _changed: () => {} } + }, + mounted() { + this._changed = this.changed.bind(this) } } diff --git a/test/getMetaInfo.test.js b/test/getMetaInfo.test.js index 55592ac..8a822ff 100644 --- a/test/getMetaInfo.test.js +++ b/test/getMetaInfo.test.js @@ -483,8 +483,6 @@ describe('getMetaInfo', () => { }) }) - // TODO: Still failing :( Child template won't be applied if child has no content as well - test('properly uses meta templates with one-level-deep nested children template', () => { Vue.component('merge-child', { render: h => h('div'), diff --git a/test/shared.test.js b/test/shared.test.js new file mode 100644 index 0000000..57d70a2 --- /dev/null +++ b/test/shared.test.js @@ -0,0 +1,64 @@ +/** + * @jest-environment node + */ +import { ensureIsArray } from '../src/shared/ensure' +import setOptions from '../src/shared/options' +import { hasGlobalWindowFn } from '../src/shared/window' +import { defaultOptions } from './utils/constants' + +const noop = () => {} + +describe('shared', () => { + test('ensureIsArray ensures var is array', () => { + let a = { p: 1 } + expect(ensureIsArray(a)).toEqual([]) + + a = 1 + expect(ensureIsArray(a)).toEqual([]) + + a = [1] + expect(ensureIsArray(a)).toBe(a) + }) + + test('ensureIsArray ensures obj prop is array', () => { + const a = { p: 1 } + expect(ensureIsArray(a, 'p')).toEqual({ p: [] }) + }) + + test('no error when window is not defined', () => { + expect(hasGlobalWindowFn()).toBe(false) + }) + + test('can use setOptions', () => { + const keyName = 'MY KEY' + let options = { keyName } + options = setOptions(options) + + expect(options.keyName).toBe(keyName) + expect(options.contentKeyName).toBeDefined() + expect(options.contentKeyName).toBe(defaultOptions.contentKeyName) + }) + + test('setOptions warns when afterNavigation not fn', () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(noop) + + let options = { afterNavigation: true } + options = setOptions(options) + + expect(warn).toHaveBeenCalledTimes(1) + expect(options.afterNavigation).toBeUndefined() + + warn.mockRestore() + }) + test('setOptions sets refreshOnceOnNavigation when afterNavigation defined', () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(noop) + + let options = { afterNavigation: noop } + options = setOptions(options) + + expect(warn).not.toHaveBeenCalled() + expect(options.refreshOnceOnNavigation).toBe(true) + + warn.mockRestore() + }) +}) diff --git a/test/utils/constants.js b/test/utils/constants.js new file mode 100644 index 0000000..a1b60bb --- /dev/null +++ b/test/utils/constants.js @@ -0,0 +1,17 @@ +import { + keyName, + attribute, + ssrAttribute, + tagIDKeyName, + metaTemplateKeyName, + contentKeyName +} from '../../src/shared/constants' + +export const defaultOptions = { + keyName, + attribute, + ssrAttribute, + tagIDKeyName, + metaTemplateKeyName, + contentKeyName +}