@ordergroove/smi-serve 1.9.8 → 1.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.9.10](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.9.9...@ordergroove/smi-serve@1.9.10) (2024-12-11)
7
+
8
+ **Note:** Version bump only for package @ordergroove/smi-serve
9
+
10
+
11
+
12
+
13
+
14
+ ## [1.9.9](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.9.8...@ordergroove/smi-serve@1.9.9) (2024-11-18)
15
+
16
+ **Note:** Version bump only for package @ordergroove/smi-serve
17
+
18
+
19
+
20
+
21
+
6
22
  ## [1.9.8](https://github.com/ordergroove/plush-toys/compare/@ordergroove/smi-serve@1.9.7...@ordergroove/smi-serve@1.9.8) (2024-11-15)
7
23
 
8
24
  **Note:** Version bump only for package @ordergroove/smi-serve
package/README.md CHANGED
@@ -67,5 +67,4 @@ Ordergroove regularly makes updates to its Subscription Manager templates to fix
67
67
  ## Limitations
68
68
 
69
69
  - Windows is not fully supported, though most functionality should work.
70
- - smi-serve only reads and writes from your currently published Subscription Manager theme. Draft themes are not currently supported.
71
70
  - SSO users and My Organization admins are not currently supported.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordergroove/smi-serve",
3
- "version": "1.9.8",
3
+ "version": "1.9.10",
4
4
  "description": "Utility to serve a Subscription Manager template locally",
5
5
  "keywords": [],
6
6
  "author": "Eugenio Lattanzio <eugenio.lattanzio@ordergroove.com>",
@@ -35,5 +35,5 @@
35
35
  "devDependencies": {
36
36
  "memfs": "^4.8.2"
37
37
  },
38
- "gitHead": "59ee10ebea327e9693e755ac765283bd49848a5d"
38
+ "gitHead": "a1708199a5638e704c6c572bae7576115d6a3a2b"
39
39
  }
package/smi-serve.spec.js CHANGED
@@ -1,66 +1,177 @@
1
- // Mock Node fs to use an in-memory filesystem
1
+ // mock Node fs to be in memory
2
2
  jest.mock('fs');
3
3
  jest.mock('fs/promises');
4
4
  const { vol } = require('memfs');
5
5
 
6
- // Mock fetch to avoid API calls and return a response with .json()
7
- jest.mock('node-fetch', () =>
8
- jest.fn(() =>
9
- Promise.resolve({
10
- json: () =>
11
- Promise.resolve({
12
- configs: { smi: { files: [{ name: 'views/main.liquid', content: '<h1>Hello</h1>' }] } },
13
- meta_fields: { dependencies: { '@ordergroove/smi-core': '0.31.6' } }
14
- })
15
- })
16
- )
17
- );
18
-
19
- jest.mock('./src/pull-theme', () => ({
20
- pullTheme: jest.fn().mockResolvedValue()
21
- }));
6
+ // mock all fetch calls
7
+ jest.mock('node-fetch', () => require('fetch-mock').sandbox());
8
+ jest.mock('inquirer');
22
9
 
23
- jest.mock('./src/select-merchant', () => ({
24
- getValidSettings: jest.fn().mockResolvedValue({
25
- token: 'mock-token',
26
- merchant: { name: 'test merchant', public_id: 'test-id', ecommerce_platform: 'shopify' }
27
- })
28
- }));
10
+ const inquirerMock = require('inquirer');
11
+ const fetchMock = require('node-fetch');
29
12
 
13
+ // don't run local dev server logic
14
+ jest.mock('./src/serve');
15
+ // mock login function to return a mock token
16
+ jest.mock('./src/login', () => ({
17
+ ...jest.requireActual('./src/login'),
18
+ login: jest.fn().mockReturnValue(Promise.resolve('mock-auth'))
19
+ }));
30
20
  jest.mock('./src/utils', () => ({
31
21
  ...jest.requireActual('./src/utils'),
32
- exec: jest.fn().mockResolvedValue()
22
+ exec: jest.fn() // don't attempt to spawn new processes
33
23
  }));
34
24
 
35
- // Mock getRC3Url in auth to avoid URL issues
36
- jest.mock('./src/auth', () => ({
37
- getRC3Url: jest.fn(() => 'https://rc3.ordergroove.com/'),
38
- getValidLoginAndCurrentMerchant: jest.fn().mockResolvedValue({
39
- token: 'mock-token',
40
- merchant: { name: 'test merchant', public_id: 'test-id' }
41
- })
42
- }));
25
+ let promptResponses = [];
26
+ function addMockPromptResponse(message, value) {
27
+ promptResponses.push({ message, value });
28
+ }
43
29
 
44
- // Fully mock serve to avoid server launch
45
- jest.mock('./src/serve', () => ({
46
- serve: jest.fn().mockResolvedValue()
47
- }));
30
+ // mock out the `inquirer.prompt` function, which takes user input
31
+ inquirerMock.prompt.mockImplementation(([prompt]) => {
32
+ const response = promptResponses.find(response => prompt.message.includes(response.message));
33
+
34
+ if (!response) {
35
+ throw Error(`no mock response defined for prompt: "${prompt.message}"`);
36
+ }
37
+
38
+ if (prompt.type === 'autocomplete') {
39
+ // call the `source` function with the mock response value, which filters the list of options
40
+ return Promise.resolve({ [prompt.name]: prompt.source(null, response.value)[0] });
41
+ }
42
+ if (prompt.type === 'list') {
43
+ // get the item in the choices array corresponding to the mock response value
44
+ const selectedOption = prompt.choices.find(choice => choice.name.includes(response.value));
45
+ if (!selectedOption) {
46
+ throw Error(`prompt response value "${response.value}" not found in list of choices`);
47
+ }
48
+ return Promise.resolve({ [prompt.name]: selectedOption.value });
49
+ }
50
+
51
+ throw Error(`no mock response defined for prompt type "${prompt.type}"`);
52
+ });
48
53
 
49
54
  const { init } = require('./src/init');
50
55
 
51
- // Basic unit test for init function
52
- describe('init function', () => {
56
+ describe('smi-serve', () => {
53
57
  beforeEach(() => {
54
- // Clear in-memory filesystem before each test
58
+ // wipe mock filesystem
55
59
  vol.fromJSON({}, '/');
60
+ fetchMock.reset();
61
+ promptResponses = [];
56
62
  });
57
63
 
58
- it('should create a .gitignore file', async () => {
59
- const args = { cwd: '/', configFile: '.ogrc.json', env: 'prod' };
64
+ it('inits with expected files', async () => {
65
+ fetchMock.get('https://rc3.ordergroove.com/api/merchants/', mockSingleMerchantResponse());
66
+ fetchMock.get('https://rc3.ordergroove.com/configs/msi/?merchant_public_id=yum-id', mockMSIConfig());
67
+ fetchMock.get('https://rc3.ordergroove.com/configs/msi/themes/?merchant_public_id=yum-id', mockThemes());
68
+ fetchMock.get('https://static.ordergroove.com/@ordergroove/smi-core/0.31.6/package.json', {
69
+ devDependencies: {
70
+ '@ordergroove/smi-templates': '^0.40.1'
71
+ }
72
+ });
73
+ fetchMock.get(
74
+ 'https://rc3.ordergroove.com/configs/msi/drafts/?merchant_public_id=yum-id&main_theme=main-theme-id',
75
+ mockDraft()
76
+ );
60
77
 
61
- await init(args);
78
+ addMockPromptResponse('Select a theme', 'main');
79
+ addMockPromptResponse('Pulling theme will overwrite existing files', 'Overwrite existing theme files');
80
+
81
+ await init({ cwd: '/', configFile: '.ogrc.json', env: 'prod' });
62
82
 
63
83
  const files = vol.toJSON();
64
- expect(files['/.gitignore']).toBeDefined();
84
+ for (const file of Object.keys(files)) {
85
+ if (file.endsWith('.json')) {
86
+ files[file] = JSON.parse(files[file]);
87
+ }
88
+ }
89
+ expect(files).toEqual({
90
+ '/.gitignore': '.ogrc.json\nnode_modules/\n',
91
+ '/.ogrc.json': {
92
+ prod: {
93
+ token: 'mock-auth',
94
+ merchant: {
95
+ ecommerce_platform: 'shopify',
96
+ name: 'test merchant',
97
+ public_id: 'yum-id',
98
+ selectedTheme: {
99
+ id: 'main-theme-id',
100
+ name: 'main'
101
+ }
102
+ }
103
+ }
104
+ },
105
+ '/package.json': {
106
+ author: '',
107
+ description: 'Ordergroove Subscription Manager for test merchant on shopify platform (yum-id)}',
108
+ keywords: ['Ordergroove Subscription Manager', 'test merchant', 'yum-id'],
109
+ main: 'views/main.liquid',
110
+ ordergroove: {
111
+ coreVersion: '0.31.6',
112
+ templatesVersion: '0.40.1'
113
+ },
114
+ scripts: {
115
+ start: 'smi-serve'
116
+ }
117
+ },
118
+ '/styles/main.less': '* { color: red; }',
119
+ '/views/main.liquid': '<h1>Hello world</h1>'
120
+ });
65
121
  });
66
122
  });
123
+
124
+ function mockSingleMerchantResponse() {
125
+ return [
126
+ {
127
+ name: 'test merchant',
128
+ public_id: 'yum-id',
129
+ ecommerce_platform: 'shopify'
130
+ }
131
+ ];
132
+ }
133
+
134
+ function mockMSIConfig() {
135
+ return {
136
+ ...mockDraft(),
137
+ main_theme: 'main-theme-id'
138
+ };
139
+ }
140
+
141
+ function mockThemes() {
142
+ return {
143
+ results: [
144
+ {
145
+ id: 'main-theme-id',
146
+ name: 'main'
147
+ }
148
+ ]
149
+ };
150
+ }
151
+
152
+ function mockDraft() {
153
+ return {
154
+ configs: {
155
+ smi: {
156
+ files: [
157
+ {
158
+ name: '/views/main.liquid',
159
+ content: '<h1>Hello world</h1>'
160
+ },
161
+ {
162
+ name: '/styles/main.less',
163
+ content: '* { color: red; }'
164
+ }
165
+ ]
166
+ },
167
+ provisioned_with: {
168
+ '@ordergroove/smi-templates': '0.40.1'
169
+ }
170
+ },
171
+ meta_fields: {
172
+ dependencies: {
173
+ '@ordergroove/smi-core': '0.31.6'
174
+ }
175
+ }
176
+ };
177
+ }
package/src/pull-theme.js CHANGED
@@ -16,7 +16,7 @@ async function getThemeList(args) {
16
16
 
17
17
  // Fetch theme data for the selected merchant
18
18
  const themeResponse = await fetch(
19
- `${getRC3Url(args)}/configs/msi/themes/?merchant_public_id=${merchant.public_id}`,
19
+ `${getRC3Url(args)}configs/msi/themes/?merchant_public_id=${merchant.public_id}`,
20
20
  {
21
21
  headers: { Authorization: `Bearer ${token}` }
22
22
  }
@@ -46,7 +46,7 @@ async function getMerchantThemes(args) {
46
46
  const { token, merchant } = await getValidSettings(args);
47
47
 
48
48
  // Fetch the msi settings which contains main_theme
49
- const msiSettings = await fetch(`${getRC3Url(args)}/configs/msi/?merchant_public_id=${merchant.public_id}`, {
49
+ const msiSettings = await fetch(`${getRC3Url(args)}configs/msi/?merchant_public_id=${merchant.public_id}`, {
50
50
  headers: { Authorization: `Bearer ${token}` }
51
51
  });
52
52
 
@@ -85,7 +85,7 @@ async function fetchThemeFiles(merchantPublicId, themeId, args) {
85
85
  const { token } = await getValidSettings(args);
86
86
 
87
87
  const themeResponse = await fetch(
88
- `${getRC3Url(args)}/configs/msi/drafts/?merchant_public_id=${merchantPublicId}&main_theme=${themeId}`,
88
+ `${getRC3Url(args)}configs/msi/drafts/?merchant_public_id=${merchantPublicId}&main_theme=${themeId}`,
89
89
  {
90
90
  headers: { Authorization: `Bearer ${token}` }
91
91
  }