UNPKG

apostrophe

Version:
333 lines (322 loc) • 9.49 kB
const t = require('../test-lib/test.js'); const assert = require('assert'); const _ = require('lodash'); const { createId } = require('@paralleldrive/cuid2'); const cleanup = []; const shortName = `test-${createId()}`; after(async () => { for (const apos of cleanup) { await t.destroy(apos); } }); describe('add missing schema fields', function() { this.timeout(t.timeout); it('first generation & sanity checks', async function() { const apos = await t.create({ root: module, shortName, modules: { product: { extend: '@apostrophecms/piece-type', options: { alias: 'product' }, fields: { add: { price: { type: 'integer', required: true, def: 10 }, body: { type: 'area', options: { widgets: { hero: {}, '@apostrophecms/rich-text': {} } } } } } }, 'hero-widget': { extend: '@apostrophecms/widget-type', fields: { add: { label: { type: 'string' } } } } } }); cleanup.push(apos); const product = apos.product.newInstance(); product.title = 'Product 1'; // Intentionally not equal to the default product.price = 15; product.body = { metaType: 'area', items: [ { type: 'hero', label: 'Sample Label' } ] }; const req = apos.task.getReq(); await apos.product.insert(req, product); const products = await apos.doc.db.find({ type: 'product' }).sort({ aposMode: 1 }).toArray(); assert.strictEqual(products.length, 2); for (const product of products) { // Not a thing yet assert.strictEqual(product.code, undefined); assert.strictEqual(product.title, 'Product 1'); assert.strictEqual(product.price, 15); assert.strictEqual(product.body.items[0].label, 'Sample Label'); } }); it('second generation schema (objects, arrays, new widget fields)', async function() { const apos = await t.create({ root: module, // Same on purpose so we reuse the database shortName, modules: { product: { extend: '@apostrophecms/piece-type', options: { alias: 'product' }, fields: { add: { price: { type: 'float', required: true }, body: { type: 'area', options: { widgets: { hero: {}, '@apostrophecms/rich-text': {} } } }, code: { type: 'integer', required: true, def: 20 }, attitude: { // Test the fallback default of the type type: 'string' }, addresses: { type: 'array', fields: { add: { street: { type: 'string' } } } }, flavors: { type: 'object', fields: { add: { chocolate: { type: 'boolean', def: true }, vanilla: { type: 'boolean', def: false }, strawberry: { type: 'boolean' // should get fallback default } } } } } } }, 'hero-widget': { extend: '@apostrophecms/widget-type', fields: { add: { label: { type: 'string' }, // New properties, with defaults sublabel: { type: 'string', def: 'Default Sublabel' }, specialized: { type: 'boolean', def: false } } } } } }); cleanup.push(apos); const products = await apos.doc.db.find({ type: 'product' }).sort({ aposMode: 1 }).toArray(); assert.strictEqual(products.length, 2); for (const product of products) { assert.strictEqual(product.code, 20); assert.strictEqual(product.title, 'Product 1'); // Still unchanged from existing non-default value assert.strictEqual(product.price, 15); assert.strictEqual(product.attitude, ''); assert(_.isEqual(product.addresses, [])); assert.strictEqual(product.flavors.chocolate, true); assert.strictEqual(product.flavors.vanilla, false); // Type has no fallback default (update test if this changes) assert.strictEqual(product.flavors.strawberry, undefined); assert.strictEqual(product.body.items[0].label, 'Sample Label'); assert.strictEqual(product.body.items[0].sublabel, 'Default Sublabel'); assert.strictEqual(product.body.items[0].specialized, false); } const product = products.find(product => product.aposMode === 'draft'); const req = apos.task.getReq({ mode: 'draft' }); product.addresses.push({ street: '1168 E Passyunk Ave' }); const result = await apos.product.update(req, product); await apos.product.publish(req, result); assert.strictEqual(result.addresses.length, 1); assert.strictEqual(result.addresses[0].street, '1168 E Passyunk Ave'); }); it('third generation schema (new array fields & verify new defaults do not crush existing values)', async function() { const apos = await t.create({ root: module, // Same on purpose so we reuse the database shortName, modules: { product: { extend: '@apostrophecms/piece-type', options: { alias: 'product' }, fields: { add: { price: { type: 'float', required: true }, body: { type: 'area', options: { widgets: { hero: {}, '@apostrophecms/rich-text': {} } } }, code: { type: 'integer', required: true, def: 20 }, addresses: { type: 'array', fields: { add: { street: { type: 'string' }, city: { type: 'string', def: 'Philadelphia' } } } }, flavors: { type: 'object', fields: { add: { chocolate: { type: 'boolean', def: true }, vanilla: { type: 'boolean', // Verify change of default does NOT mess up an existing // property def: true }, strawberry: { type: 'boolean' // should get fallback default } } } } } } }, 'hero-widget': { extend: '@apostrophecms/widget-type', fields: { add: { label: { type: 'string' }, // New properties, with defaults sublabel: { type: 'string', def: 'Default Sublabel' }, specialized: { type: 'boolean', def: false } } } } } }); cleanup.push(apos); const products = await apos.doc.db.find({ type: 'product', aposMode: { $in: [ 'draft', 'published' ] } }).sort({ aposMode: 1 }).toArray(); assert.strictEqual(products.length, 2); for (const product of products) { assert.strictEqual(product.code, 20); assert.strictEqual(product.title, 'Product 1'); assert.strictEqual(product.addresses.length, 1); // Should NOT change because it already has a value assert.strictEqual(product.flavors.vanilla, false); assert.strictEqual(product.addresses[0].street, '1168 E Passyunk Ave'); assert.strictEqual(product.addresses[0].city, 'Philadelphia'); } // Direct invocation to make sure an additional invocation with the same // schemas does no new work const { scans, updates } = await apos.migration.addMissingSchemaFields(); assert.strictEqual(scans, 0); assert.strictEqual(updates, 0); }); });