Skip to content

Commit

Permalink
Merge pull request #39 from enspandi/auto-discovery
Browse files Browse the repository at this point in the history
Support auto discovery for mirage and ember-data entities via import.meta.glob
  • Loading branch information
NullVoxPopuli authored Nov 18, 2024
2 parents 78ce33b + 1b9af89 commit c5ead8b
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 67 deletions.
35 changes: 35 additions & 0 deletions ember-mirage/src/create-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { _utilsInflectorCamelize as camelize } from 'miragejs';

async function getDefaultExports(modules) {
return Promise.all(
Object.entries(modules).map(([path, importFn]) =>
importFn().then((module) => ({ path, defaultExport: module.default })),
),
);
}

export function entityName(path) {
const filenameWithoutExt = path.split('/').pop().split('.')[0];
return camelize(filenameWithoutExt);
}

export async function createConfig(mirageImportMap = {}) {
return {
factories: await importEntities(mirageImportMap.factories),
fixtures: await importEntities(mirageImportMap.fixtures),
models: await importEntities(mirageImportMap.models),
scenarios: await importEntities(mirageImportMap.scenarios),
serializers: await importEntities(mirageImportMap.serializers),
identityManagers: await importEntities(mirageImportMap.identityManagers),
};
}

async function importEntities(importMap = {}) {
const modules = await getDefaultExports(importMap);

return modules.reduce((acc, { path, defaultExport }) => {
const configName = entityName(path);
acc[configName] = defaultExport;
return acc;
}, {});
}
113 changes: 113 additions & 0 deletions ember-mirage/src/ember-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Model, belongsTo, hasMany } from 'miragejs';
import EmberDataSerializer from './ember-data-serializer';
import { entityName } from './create-config';

const MirageModelCache = {};
const MirageSerializerCache = {};

export function importEmberDataModels(store, importMap = {}) {
return Object.keys(importMap).reduce((acc, path) => {
const configName = entityName(path);
acc[configName] = createMirageModel(store, configName);
return acc;
}, {});
}

// TODO: Replace with original `applyEmberDataSerializers` function
export function importEmberDataSerializers(
mirageSerializers,
emberDataConfig = {},
) {
const { store, serializers = {} } = emberDataConfig;
return Object.keys(serializers).reduce((acc, path) => {
const configName = entityName(path);
acc[configName] = createMirageSerializer(
store,
mirageSerializers,
configName,
);
return acc;
}, {});
}

function createMirageModel(store, modelName) {
if (MirageModelCache[modelName]) {
return MirageModelCache[modelName];
}

const model = store.modelFor(modelName);
const relationships = {};

model.eachRelationship((name, r) => {
if (r.kind === 'belongsTo') {
relationships[name] = belongsTo(r.type, r.options);
} else if (r.kind === 'hasMany') {
relationships[name] = hasMany(r.type, r.options);
}
});
const mirageModel = Model.extend(relationships);

MirageModelCache[modelName] = mirageModel;

return mirageModel;
}

export function createMirageSerializer(store, serializers, serializerName) {
if (MirageSerializerCache[serializerName]) {
return MirageSerializerCache[serializerName];
}

let mirageSerializer =
serializers[serializerName] ||
serializers.application ||
EmberDataSerializer;

let dsSerializer = store.serializerFor(serializerName);

let transforms;
let primaryKey = dsSerializer.primaryKey;
let attrs = dsSerializer.attrs;
if (primaryKey || attrs) {
if (attrs) {
let serializer = mirageSerializer.create
? mirageSerializer.create()
: new mirageSerializer();

transforms = serializer.transforms || {};

Object.keys(attrs).forEach((key) => {
let transform = attrs[key];
let serializerTransform = serializer.transforms
? serializer.transforms[key]
: {};
let resolvedTransform =
typeof attrs[key] === 'string'
? {
key: attrs[key],
}
: {
key: attrs[key].key,
};

if (transform.serialize !== undefined) {
resolvedTransform.deserialize = transform.serialize;
}

if (transform.deserialize !== undefined) {
resolvedTransform.serialize = transform.deserialize;
}

transforms[key] = Object.assign(resolvedTransform, serializerTransform);
});
}

mirageSerializer = mirageSerializer.extend({
primaryKey,
transforms,
});
}

MirageSerializerCache[serializerName] = mirageSerializer;

return mirageSerializer;
}
2 changes: 1 addition & 1 deletion ember-mirage/src/index.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { default as startMirage } from './start-mirage';
export { createConfig, entityName } from './create-config.js';
44 changes: 0 additions & 44 deletions ember-mirage/src/start-mirage.js

This file was deleted.

30 changes: 18 additions & 12 deletions ember-mirage/src/test-support/setup-mirage.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import { assert } from '@ember/debug';
import { settled } from '@ember/test-helpers';
import { createServer as _createServer } from 'miragejs';

import startMirage from '../start-mirage';
export function setupMirage(hooks = self, { createServer, config }) {
assert(
`Unexpected arity for setupMirage. Expected 2 (hooks, { createServer, or config })`,
arguments.length <= 2 && arguments.length > 0,
);
assert(
`Second argument to setupMirage must be an object and not null`,
typeof arguments[1] === 'object' && arguments[1] !== null,
);
assert(
`Second argument to setupMirage must on or both of createServer and/or config. You passed ${Object.keys(arguments[1]).join(', ')}`,
'createServer' in arguments[1] || 'config' in arguments[1],
);

/**
Used to set up mirage for a test. Must be called after one of the
`ember-qunit` `setup*Test()` methods. It starts the server and sets
`this.server` to point to it, and shuts the server down when the test
finishes.
createServer ??= _createServer;

NOTE: the `hooks = self` is for mocha support
@hide
*/
export function setupMirage(hooks = self, { makeServer, ...options }) {
hooks.beforeEach(function () {
hooks.beforeEach(async function () {
if (!this.owner) {
throw new Error(
'You must call one of the ember-qunit setupTest(),' +
Expand All @@ -21,7 +27,7 @@ export function setupMirage(hooks = self, { makeServer, ...options }) {
);
}

this.server = startMirage(makeServer, { owner: this.owner, ...options });
this.server = await createServer(config ?? {});
});

hooks.afterEach(function () {
Expand Down
32 changes: 32 additions & 0 deletions test-app/app/mirage/servers/create-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//import { createConfig } from 'ember-mirage';
//import defaultRoutes from '../routes/default';
//
//export async function config(store) {
// const { importEmberDataModels, importEmberDataSerializers } = await import(
// 'ember-mirage/ember-data'
// );
//
// const mirageConfig = await createConfig({
// factories: import.meta.glob('./factories/*'),
// fixtures: import.meta.glob('./fixtures/*'),
// // Don't import our mirage things that will be auto-discovered
// // models: import.meta.glob('./models/*'),
// // serializers: import.meta.glob('./serializers/*'),
// identityManagers: import.meta.glob('./identity-managers/*'),
// });
//
// return {
// ...mirageConfig,
// models: {
// // use ember-data model auto discovery
// ...importEmberDataModels(store, import.meta.glob('../models/*')),
// ...mirageConfig.models,
// },
// // apply ember-data serializer config details to mirage serializers
// serializers: importEmberDataSerializers(store, mirageConfig.serializers),
// routes() {
// this.config({ routes: defaultRoutes });
// },
// };
//}
//
Empty file.
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { createServer } from 'miragejs';

import factories from '../factories';
import mirageModels from '../models';
import defaultRoutes from '../routes/default';
Expand Down Expand Up @@ -28,7 +26,7 @@ export default function (config) {
},
};

return createServer(finalConfig);
return finalConfig;
}

function defineRoutes() {
Expand Down
16 changes: 14 additions & 2 deletions test-app/tests/acceptance/example-test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { currentURL, visit } from '@ember/test-helpers';
import { createServer } from 'miragejs';
import { module, test } from 'qunit';

import mirageConfig from 'test-app/mirage/servers/default';
import mirageConfig from 'test-app/mirage/servers/manual';
import { setupApplicationTest } from 'test-app/tests/helpers';

import { setupMirage } from 'test-app/tests/test-support/mirage';
Expand All @@ -11,6 +12,7 @@ module('Acceptance | example test', function (hooks) {

module('Test with default config', function (hooks) {
setupMirage(hooks);

test('visiting /example', async function (assert) {
await visit('/example');

Expand All @@ -19,7 +21,17 @@ module('Acceptance | example test', function (hooks) {
});

module('Test with imported config', function (hooks) {
setupMirage(hooks, { makeserver: mirageConfig });
setupMirage(hooks, { config: mirageConfig });
test('visiting /example', async function (assert) {
await visit('/example');

assert.strictEqual(currentURL(), '/example');
});
});

module('Test with custom server', function (hooks) {
setupMirage(hooks, { createServer: () => createServer(mirageConfig) });

test('visiting /example', async function (assert) {
await visit('/example');

Expand Down
11 changes: 6 additions & 5 deletions test-app/tests/test-support/mirage.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import mirageConfig from 'test-app/mirage/servers/default';
import mirageConfig from 'test-app/mirage/servers/manual';

import { setupMirage as _setupMirage } from 'ember-mirage/test-support';

export function setupMirage(hooks, options) {
options = options || {};
options.makeServer = options.makeServer || mirageConfig;
import { createServer as _createServer } from 'miragejs';

return _setupMirage(hooks, options);
export function setupMirage(hooks, { createServer, config } = {}) {
createServer ??= _createServer;
config ??= mirageConfig;
return _setupMirage(hooks, { createServer, config });
}

0 comments on commit c5ead8b

Please sign in to comment.