@exdst-sitecore-content-sdk/astro 0.0.1
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/LICENSE.txt +202 -0
- package/README.md +3 -0
- package/package.json +101 -0
- package/src/client/index.ts +12 -0
- package/src/client/sitecore-astro-client.test.ts +271 -0
- package/src/client/sitecore-astro-client.ts +137 -0
- package/src/components/AstroImage.astro +114 -0
- package/src/components/Date.astro +76 -0
- package/src/components/DefaultEmptyFieldEditingComponentImage.astro +24 -0
- package/src/components/DefaultEmptyFieldEditingComponentText.astro +12 -0
- package/src/components/EditingScripts.astro +49 -0
- package/src/components/EmptyRendering.astro +3 -0
- package/src/components/ErrorBoundary.astro +77 -0
- package/src/components/FieldMetadata.astro +30 -0
- package/src/components/File.astro +46 -0
- package/src/components/HiddenRendering.astro +22 -0
- package/src/components/Image.astro +155 -0
- package/src/components/Link.astro +105 -0
- package/src/components/MissingComponent.astro +39 -0
- package/src/components/Placeholder/EmptyPlaceholder.astro +9 -0
- package/src/components/Placeholder/Placeholder.astro +100 -0
- package/src/components/Placeholder/PlaceholderMetadata.astro +102 -0
- package/src/components/Placeholder/PlaceholderUtils.astro +153 -0
- package/src/components/Placeholder/index.ts +5 -0
- package/src/components/Placeholder/models.ts +82 -0
- package/src/components/Placeholder/placeholder-utils.test.ts +162 -0
- package/src/components/Placeholder/placeholder-utils.ts +80 -0
- package/src/components/RenderWrapper.astro +31 -0
- package/src/components/RichText.astro +59 -0
- package/src/components/Text.astro +97 -0
- package/src/components/sharedTypes/index.ts +1 -0
- package/src/components/sharedTypes/props.ts +17 -0
- package/src/config/define-config.test.ts +526 -0
- package/src/config/define-config.ts +99 -0
- package/src/config/index.ts +1 -0
- package/src/config-cli/define-cli-config.test.ts +95 -0
- package/src/config-cli/define-cli-config.ts +50 -0
- package/src/config-cli/index.ts +1 -0
- package/src/context.ts +68 -0
- package/src/editing/constants.ts +8 -0
- package/src/editing/editing-config-middleware.test.ts +166 -0
- package/src/editing/editing-config-middleware.ts +111 -0
- package/src/editing/editing-render-middleware.test.ts +801 -0
- package/src/editing/editing-render-middleware.ts +288 -0
- package/src/editing/index.ts +16 -0
- package/src/editing/render-middleware.test.ts +57 -0
- package/src/editing/render-middleware.ts +51 -0
- package/src/editing/utils.test.ts +852 -0
- package/src/editing/utils.ts +308 -0
- package/src/enhancers/WithEmptyFieldEditingComponent.astro +56 -0
- package/src/enhancers/WithFieldMetadata.astro +31 -0
- package/src/env.d.ts +12 -0
- package/src/index.ts +16 -0
- package/src/middleware/index.ts +24 -0
- package/src/middleware/middleware.test.ts +507 -0
- package/src/middleware/middleware.ts +167 -0
- package/src/middleware/multisite-middleware.test.ts +672 -0
- package/src/middleware/multisite-middleware.ts +147 -0
- package/src/middleware/robots-middleware.test.ts +113 -0
- package/src/middleware/robots-middleware.ts +47 -0
- package/src/middleware/sitemap-middleware.test.ts +152 -0
- package/src/middleware/sitemap-middleware.ts +65 -0
- package/src/services/component-props-service.ts +182 -0
- package/src/sharedTypes/component-props.ts +17 -0
- package/src/site/index.ts +1 -0
- package/src/test-data/components/Bar.astro +0 -0
- package/src/test-data/components/Baz.astro +0 -0
- package/src/test-data/components/Foo.astro +0 -0
- package/src/test-data/components/Hero.variant.astro +0 -0
- package/src/test-data/components/NotComponent.bsx +0 -0
- package/src/test-data/components/Qux.astro +0 -0
- package/src/test-data/components/folded/Folded.astro +0 -0
- package/src/test-data/components/folded/random-file-2.docx +0 -0
- package/src/test-data/components/random-file.txt +0 -0
- package/src/test-data/helpers.ts +46 -0
- package/src/test-data/personalizeData.ts +63 -0
- package/src/tools/generate-map.ts +83 -0
- package/src/tools/index.ts +8 -0
- package/src/tools/templating/components.test.ts +305 -0
- package/src/tools/templating/components.ts +49 -0
- package/src/tools/templating/constants.ts +4 -0
- package/src/tools/templating/default-component.test.ts +31 -0
- package/src/tools/templating/default-component.ts +63 -0
- package/src/tools/templating/index.ts +2 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/utils.test.ts +48 -0
- package/src/utils/utils.ts +52 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
/* eslint-disable no-unused-expressions */
|
|
2
|
+
/* eslint-disable dot-notation */
|
|
3
|
+
import * as chai from 'chai';
|
|
4
|
+
import { use } from 'chai';
|
|
5
|
+
import chaiString from 'chai-string';
|
|
6
|
+
import sinonChai from 'sinon-chai';
|
|
7
|
+
import sinon, { spy } from 'sinon';
|
|
8
|
+
import { debug } from '@sitecore-content-sdk/core';
|
|
9
|
+
|
|
10
|
+
import { MultisiteMiddleware } from './multisite-middleware';
|
|
11
|
+
import { SiteInfo, SiteResolver } from '@sitecore-content-sdk/core/site';
|
|
12
|
+
import { APIContext, AstroCookieSetOptions } from 'astro';
|
|
13
|
+
|
|
14
|
+
use(sinonChai);
|
|
15
|
+
const expect = chai.use(chaiString).expect;
|
|
16
|
+
|
|
17
|
+
describe('MultisiteMiddleware', () => {
|
|
18
|
+
let debugSpy;
|
|
19
|
+
const validateDebugLog = (message: string, ...params: any) =>
|
|
20
|
+
expect(debugSpy.args.find((log) => log[0] === message)).to.deep.equal([message, ...params]);
|
|
21
|
+
const validateEndMessageDebugLog = (message: string, params: any) => {
|
|
22
|
+
const logParams = debugSpy.args.find((log) => log[0] === message) as Array<unknown>;
|
|
23
|
+
|
|
24
|
+
expect(logParams[2]).to.deep.include(params);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const siteName = 'foo';
|
|
28
|
+
const hostname = 'http://test.test/styleguide';
|
|
29
|
+
|
|
30
|
+
const defaultConfig = {
|
|
31
|
+
sites: [],
|
|
32
|
+
enabled: true,
|
|
33
|
+
useCookieResolution: () => false,
|
|
34
|
+
defaultHostname: '',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const createContext = (props: any = {}) => {
|
|
38
|
+
const context = {
|
|
39
|
+
request: {
|
|
40
|
+
url: props.url || new URL(hostname),
|
|
41
|
+
headers: {
|
|
42
|
+
get(key: string) {
|
|
43
|
+
const headers = {
|
|
44
|
+
host: 'foo.net',
|
|
45
|
+
...props.headerValues,
|
|
46
|
+
};
|
|
47
|
+
return headers[key];
|
|
48
|
+
},
|
|
49
|
+
append(key: string, value: string | Record<string, any>) {
|
|
50
|
+
context.request.headers[key] = value;
|
|
51
|
+
},
|
|
52
|
+
...props.headers,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
cookies: {
|
|
56
|
+
get(cookieName: string) {
|
|
57
|
+
const cookies = { ...props?.cookieValues };
|
|
58
|
+
return cookies[cookieName] ? { value: cookies[cookieName] } : undefined;
|
|
59
|
+
},
|
|
60
|
+
set(
|
|
61
|
+
cookieName: string,
|
|
62
|
+
value: string | Record<string, any>,
|
|
63
|
+
options?: AstroCookieSetOptions
|
|
64
|
+
) {
|
|
65
|
+
context.cookies[cookieName] = { value, ...options };
|
|
66
|
+
},
|
|
67
|
+
...props?.cookies,
|
|
68
|
+
...props.cookieValues,
|
|
69
|
+
},
|
|
70
|
+
locals: props.locals || {},
|
|
71
|
+
url: props.url || new URL(hostname),
|
|
72
|
+
currentLocale: props.currentLocale,
|
|
73
|
+
preferredLocale: props.preferredLocale,
|
|
74
|
+
// eslint-disable-next-line no-unused-vars
|
|
75
|
+
rewrite: (_) => props.response,
|
|
76
|
+
} as APIContext;
|
|
77
|
+
|
|
78
|
+
return context;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const createResponse = (props: any = {}) => {
|
|
82
|
+
const response = {
|
|
83
|
+
...props,
|
|
84
|
+
headers: {
|
|
85
|
+
get(key: string) {
|
|
86
|
+
const headers = {
|
|
87
|
+
...response.headers,
|
|
88
|
+
...props.headerValues,
|
|
89
|
+
};
|
|
90
|
+
return headers[key];
|
|
91
|
+
},
|
|
92
|
+
append(key: string, value: string | Record<string, any>) {
|
|
93
|
+
response.headers[key] = value;
|
|
94
|
+
},
|
|
95
|
+
...props.headers,
|
|
96
|
+
},
|
|
97
|
+
} as Response;
|
|
98
|
+
|
|
99
|
+
Object.defineProperties(response.headers, {
|
|
100
|
+
forEach: {
|
|
101
|
+
value: (cb) => {
|
|
102
|
+
Object.keys(response.headers).forEach((key) =>
|
|
103
|
+
cb(response.headers[key], key, response.headers)
|
|
104
|
+
);
|
|
105
|
+
},
|
|
106
|
+
enumerable: false,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return response;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const createMiddleware = (input: { [key: string]: any; siteResolver?: SiteResolver } = {}) => {
|
|
114
|
+
const props = { ...defaultConfig, ...input.config };
|
|
115
|
+
class MockSiteResolver extends SiteResolver {
|
|
116
|
+
getByName = sinon.stub().returns({
|
|
117
|
+
name: siteName,
|
|
118
|
+
language: input.language || '',
|
|
119
|
+
hostName: input.hostName,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
getByHost = sinon.stub().returns({
|
|
123
|
+
name: siteName,
|
|
124
|
+
language: input.language || '',
|
|
125
|
+
hostName: input.hostName,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const siteResolver = new MockSiteResolver([]);
|
|
130
|
+
const middleware = new MultisiteMiddleware({
|
|
131
|
+
...props,
|
|
132
|
+
});
|
|
133
|
+
middleware['siteResolver'] = siteResolver;
|
|
134
|
+
|
|
135
|
+
return { middleware, siteResolver };
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
before(() => {
|
|
139
|
+
debugSpy = spy(debug, 'multisite');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
afterEach(() => {
|
|
143
|
+
debugSpy.resetHistory();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('request skipped', () => {
|
|
147
|
+
describe('disabled / skip', () => {
|
|
148
|
+
const res = createResponse();
|
|
149
|
+
|
|
150
|
+
const test = async (pathname: string, middleware: MultisiteMiddleware) => {
|
|
151
|
+
const context = createContext({
|
|
152
|
+
url: {
|
|
153
|
+
pathname,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const mockNext = async () => res;
|
|
158
|
+
|
|
159
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
160
|
+
const isDisabledGlobally = middleware['config'].enabled === false;
|
|
161
|
+
|
|
162
|
+
if (!isDisabledGlobally) {
|
|
163
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
164
|
+
pathname,
|
|
165
|
+
language: 'en',
|
|
166
|
+
hostname: 'foo.net',
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const message = isDisabledGlobally
|
|
171
|
+
? 'skipped (multisite middleware is disabled globally)'
|
|
172
|
+
: 'skipped (multisite middleware is disabled)';
|
|
173
|
+
validateDebugLog(message);
|
|
174
|
+
|
|
175
|
+
expect(finalRes).to.deep.equal(res);
|
|
176
|
+
|
|
177
|
+
debugSpy.resetHistory();
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
it('default', async () => {
|
|
181
|
+
const { middleware } = createMiddleware();
|
|
182
|
+
|
|
183
|
+
await test('/src/image.png', middleware);
|
|
184
|
+
await test('/api/layout/render', middleware);
|
|
185
|
+
await test('/sitecore/render', middleware);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should apply both default and custom rules when custom disabled function provided', async () => {
|
|
189
|
+
const skip = (context: APIContext) => context.url.pathname === '/crazypath/luna';
|
|
190
|
+
|
|
191
|
+
const { middleware } = createMiddleware({
|
|
192
|
+
config: { ...defaultConfig, skip },
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await test('/src/image.png', middleware);
|
|
196
|
+
await test('/api/layout/render', middleware);
|
|
197
|
+
await test('/sitecore/render', middleware);
|
|
198
|
+
await test('/crazypath/luna', middleware);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('disabled in chain', () => {
|
|
203
|
+
it('should skip if skipMiddleware local variable is true', async () => {
|
|
204
|
+
const { middleware } = createMiddleware();
|
|
205
|
+
const res = createResponse();
|
|
206
|
+
|
|
207
|
+
const context = createContext({
|
|
208
|
+
locals: {
|
|
209
|
+
skipMiddleware: true,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const mockNext = async () => res;
|
|
214
|
+
|
|
215
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
216
|
+
|
|
217
|
+
validateDebugLog(
|
|
218
|
+
'skipped (multisite middleware is disabled by one of the previous middlewares)'
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
expect(finalRes).to.deep.equal(res);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// describe('preview', () => {
|
|
227
|
+
// it('preview data cookie is present', async () => {
|
|
228
|
+
// const { middleware } = createMiddleware();
|
|
229
|
+
// const res = createResponse();
|
|
230
|
+
|
|
231
|
+
// const context = createContext({
|
|
232
|
+
// url: new URL(hostname),
|
|
233
|
+
// cookieValues: {
|
|
234
|
+
// _preview_data: true,
|
|
235
|
+
// },
|
|
236
|
+
// });
|
|
237
|
+
|
|
238
|
+
// const mockNext = async () => {
|
|
239
|
+
// return res;
|
|
240
|
+
// };
|
|
241
|
+
|
|
242
|
+
// const finalRes = await middleware.handle(context, mockNext);
|
|
243
|
+
|
|
244
|
+
// validateDebugLog('skipped (preview)');
|
|
245
|
+
|
|
246
|
+
// const resCookies = (finalRes as Response).headers.getSetCookie();
|
|
247
|
+
// expect(resCookies).to.contain('_preview_data=true');
|
|
248
|
+
// });
|
|
249
|
+
// });
|
|
250
|
+
|
|
251
|
+
describe('Sitecore Preview', () => {
|
|
252
|
+
it('request is passed', async () => {
|
|
253
|
+
const context = createContext({
|
|
254
|
+
cookieValues: { sc_site: 'foobar', sc_preview: 'true' },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const mockNext = sinon.stub().returns(
|
|
258
|
+
createResponse({
|
|
259
|
+
headers: [],
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const { middleware, siteResolver } = createMiddleware({
|
|
264
|
+
config: { ...defaultConfig, useCookieResolution: () => true },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
268
|
+
|
|
269
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
270
|
+
pathname: '/styleguide',
|
|
271
|
+
language: 'en',
|
|
272
|
+
hostname: 'foo.net',
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
276
|
+
rewritePath: '/_site_foobar/styleguide',
|
|
277
|
+
siteName: 'foobar',
|
|
278
|
+
headers: {
|
|
279
|
+
...finalRes.headers,
|
|
280
|
+
'x-sc-rewrite': '/_site_foobar/styleguide',
|
|
281
|
+
},
|
|
282
|
+
cookies: 'sc_site=foobar; HttpOnly; Secure; SameSite=None',
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
expect(siteResolver.getByHost.called).to.be.false;
|
|
286
|
+
expect(siteResolver.getByName.called).to.be.false;
|
|
287
|
+
|
|
288
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foobar/styleguide' }));
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('should not be skipped if multisite middleware is disabled globally', async () => {
|
|
292
|
+
const context = createContext({
|
|
293
|
+
cookieValues: { sc_site: 'foobar', sc_preview: 'true' },
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const mockNext = sinon.stub().returns(
|
|
297
|
+
createResponse({
|
|
298
|
+
headers: [],
|
|
299
|
+
})
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const { middleware, siteResolver } = createMiddleware({
|
|
303
|
+
config: { ...defaultConfig, enabled: false, useCookieResolution: () => true },
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
307
|
+
|
|
308
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
309
|
+
pathname: '/styleguide',
|
|
310
|
+
language: 'en',
|
|
311
|
+
hostname: 'foo.net',
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
315
|
+
rewritePath: '/_site_foobar/styleguide',
|
|
316
|
+
siteName: 'foobar',
|
|
317
|
+
headers: {
|
|
318
|
+
...finalRes.headers,
|
|
319
|
+
'x-sc-rewrite': '/_site_foobar/styleguide',
|
|
320
|
+
},
|
|
321
|
+
cookies: 'sc_site=foobar; HttpOnly; Secure; SameSite=None',
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
expect(siteResolver.getByHost.called).to.be.false;
|
|
325
|
+
expect(siteResolver.getByName.called).to.be.false;
|
|
326
|
+
|
|
327
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foobar/styleguide' }));
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
describe('request passed', () => {
|
|
332
|
+
it('fallback hostname is used', async () => {
|
|
333
|
+
const context = createContext({
|
|
334
|
+
headerValues: { host: undefined },
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const mockNext = sinon.stub().returns(
|
|
338
|
+
createResponse({
|
|
339
|
+
headers: [],
|
|
340
|
+
})
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const { middleware, siteResolver } = createMiddleware({
|
|
344
|
+
config: { ...defaultConfig, defaultHostname: 'bar.net' },
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
348
|
+
|
|
349
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
350
|
+
pathname: '/styleguide',
|
|
351
|
+
language: 'en',
|
|
352
|
+
hostname: 'bar.net',
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
356
|
+
rewritePath: '/_site_foo/styleguide',
|
|
357
|
+
siteName: 'foo',
|
|
358
|
+
headers: {
|
|
359
|
+
...finalRes.headers,
|
|
360
|
+
'x-sc-rewrite': '/_site_foo/styleguide',
|
|
361
|
+
},
|
|
362
|
+
cookies: 'sc_site=foo; HttpOnly; Secure; SameSite=None',
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
expect(siteResolver.getByHost.calledWith('bar.net')).to.be.true;
|
|
366
|
+
|
|
367
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foo/styleguide' }));
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('fallback default hostName is used', async () => {
|
|
371
|
+
const context = createContext({
|
|
372
|
+
headerValues: { host: undefined },
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const mockNext = sinon.stub().returns(
|
|
376
|
+
createResponse({
|
|
377
|
+
headers: [],
|
|
378
|
+
})
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
const { middleware, siteResolver } = createMiddleware();
|
|
382
|
+
|
|
383
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
384
|
+
|
|
385
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
386
|
+
pathname: '/styleguide',
|
|
387
|
+
language: 'en',
|
|
388
|
+
hostname: 'localhost',
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
392
|
+
rewritePath: '/_site_foo/styleguide',
|
|
393
|
+
siteName: 'foo',
|
|
394
|
+
headers: {
|
|
395
|
+
...finalRes.headers,
|
|
396
|
+
'x-sc-rewrite': '/_site_foo/styleguide',
|
|
397
|
+
},
|
|
398
|
+
cookies: 'sc_site=foo; HttpOnly; Secure; SameSite=None',
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
expect(siteResolver.getByHost).to.be.calledWith('localhost');
|
|
402
|
+
|
|
403
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foo/styleguide' }));
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
it('host header is used', async () => {
|
|
407
|
+
const context = createContext();
|
|
408
|
+
|
|
409
|
+
const mockNext = sinon.stub().returns(
|
|
410
|
+
createResponse({
|
|
411
|
+
headers: [],
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
const { middleware, siteResolver } = createMiddleware();
|
|
416
|
+
|
|
417
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
418
|
+
|
|
419
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
420
|
+
pathname: '/styleguide',
|
|
421
|
+
language: 'en',
|
|
422
|
+
hostname: 'foo.net',
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
426
|
+
rewritePath: '/_site_foo/styleguide',
|
|
427
|
+
siteName: 'foo',
|
|
428
|
+
headers: {
|
|
429
|
+
...finalRes.headers,
|
|
430
|
+
'x-sc-rewrite': '/_site_foo/styleguide',
|
|
431
|
+
},
|
|
432
|
+
cookies: 'sc_site=foo; HttpOnly; Secure; SameSite=None',
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(siteResolver.getByHost).to.be.calledWith('foo.net');
|
|
436
|
+
|
|
437
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foo/styleguide' }));
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('custom response object is not provided', async () => {
|
|
441
|
+
const context = createContext();
|
|
442
|
+
|
|
443
|
+
const mockNext = sinon.stub().returns(
|
|
444
|
+
createResponse({
|
|
445
|
+
headers: [],
|
|
446
|
+
})
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
const { middleware, siteResolver } = createMiddleware({});
|
|
450
|
+
|
|
451
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
452
|
+
|
|
453
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
454
|
+
pathname: '/styleguide',
|
|
455
|
+
language: 'en',
|
|
456
|
+
hostname: 'foo.net',
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
460
|
+
rewritePath: '/_site_foo/styleguide',
|
|
461
|
+
siteName: 'foo',
|
|
462
|
+
headers: {
|
|
463
|
+
...finalRes.headers,
|
|
464
|
+
'x-sc-rewrite': '/_site_foo/styleguide',
|
|
465
|
+
},
|
|
466
|
+
cookies: 'sc_site=foo; HttpOnly; Secure; SameSite=None',
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
expect(siteResolver.getByHost).to.be.calledWith('foo.net');
|
|
470
|
+
|
|
471
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foo/styleguide' }));
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('site querystring parameter is provided', async () => {
|
|
475
|
+
const context = createContext({
|
|
476
|
+
url: new URL(hostname + '?site=qsFoo'),
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const mockNext = sinon.stub().returns(
|
|
480
|
+
createResponse({
|
|
481
|
+
headers: [],
|
|
482
|
+
})
|
|
483
|
+
);
|
|
484
|
+
|
|
485
|
+
const { middleware, siteResolver } = createMiddleware({
|
|
486
|
+
useCookieResolution: () => true,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
490
|
+
|
|
491
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
492
|
+
pathname: '/styleguide',
|
|
493
|
+
language: 'en',
|
|
494
|
+
hostname: 'foo.net',
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
498
|
+
rewritePath: '/_site_qsFoo/styleguide',
|
|
499
|
+
siteName: 'qsFoo',
|
|
500
|
+
headers: {
|
|
501
|
+
...finalRes.headers,
|
|
502
|
+
'x-sc-rewrite': '/_site_qsFoo/styleguide',
|
|
503
|
+
},
|
|
504
|
+
cookies: 'sc_site=qsFoo; HttpOnly; Secure; SameSite=None',
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
expect(siteResolver.getByHost.called).to.be.false;
|
|
508
|
+
expect(siteResolver.getByName.called).to.be.false;
|
|
509
|
+
|
|
510
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_qsFoo/styleguide' }));
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('sc_site querystring parameter is provided', async () => {
|
|
514
|
+
const context = createContext({
|
|
515
|
+
url: new URL(hostname + '?sc_site=qsFoo'),
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
const mockNext = sinon.stub().returns(
|
|
519
|
+
createResponse({
|
|
520
|
+
headers: [],
|
|
521
|
+
})
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
const { middleware, siteResolver } = createMiddleware({
|
|
525
|
+
useCookieResolution: () => true,
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
529
|
+
|
|
530
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
531
|
+
pathname: '/styleguide',
|
|
532
|
+
language: 'en',
|
|
533
|
+
hostname: 'foo.net',
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
537
|
+
rewritePath: '/_site_qsFoo/styleguide',
|
|
538
|
+
siteName: 'qsFoo',
|
|
539
|
+
headers: {
|
|
540
|
+
...finalRes.headers,
|
|
541
|
+
'x-sc-rewrite': '/_site_qsFoo/styleguide',
|
|
542
|
+
},
|
|
543
|
+
cookies: 'sc_site=qsFoo; HttpOnly; Secure; SameSite=None',
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
expect(siteResolver.getByHost.called).to.be.false;
|
|
547
|
+
expect(siteResolver.getByName.called).to.be.false;
|
|
548
|
+
|
|
549
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_qsFoo/styleguide' }));
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
it('sc_site cookie is provided and its usage enabled', async () => {
|
|
553
|
+
const context = createContext({
|
|
554
|
+
cookieValues: { sc_site: 'foobar' },
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const mockNext = sinon.stub().returns(
|
|
558
|
+
createResponse({
|
|
559
|
+
headers: [],
|
|
560
|
+
})
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
const { middleware, siteResolver } = createMiddleware({
|
|
564
|
+
config: { ...defaultConfig, useCookieResolution: () => true },
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
568
|
+
|
|
569
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
570
|
+
pathname: '/styleguide',
|
|
571
|
+
language: 'en',
|
|
572
|
+
hostname: 'foo.net',
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
576
|
+
rewritePath: '/_site_foobar/styleguide',
|
|
577
|
+
siteName: 'foobar',
|
|
578
|
+
headers: {
|
|
579
|
+
...finalRes.headers,
|
|
580
|
+
'x-sc-rewrite': '/_site_foobar/styleguide',
|
|
581
|
+
},
|
|
582
|
+
cookies: 'sc_site=foobar; HttpOnly; Secure; SameSite=None',
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
expect(siteResolver.getByHost.called).to.be.false;
|
|
586
|
+
expect(siteResolver.getByName.called).to.be.false;
|
|
587
|
+
|
|
588
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foobar/styleguide' }));
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it('sc_site cookie is provided and its usage disabled', async () => {
|
|
592
|
+
const context = createContext({
|
|
593
|
+
cookieValues: { sc_site: 'foobar' },
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
const mockNext = sinon.stub().returns(
|
|
597
|
+
createResponse({
|
|
598
|
+
headers: [],
|
|
599
|
+
})
|
|
600
|
+
);
|
|
601
|
+
|
|
602
|
+
const { middleware, siteResolver } = createMiddleware();
|
|
603
|
+
|
|
604
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
605
|
+
|
|
606
|
+
validateDebugLog('multisite middleware start: %o', {
|
|
607
|
+
pathname: '/styleguide',
|
|
608
|
+
language: 'en',
|
|
609
|
+
hostname: 'foo.net',
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
validateEndMessageDebugLog('multisite middleware end in %dms: %o', {
|
|
613
|
+
rewritePath: '/_site_foo/styleguide',
|
|
614
|
+
siteName: 'foo',
|
|
615
|
+
headers: {
|
|
616
|
+
...finalRes.headers,
|
|
617
|
+
'x-sc-rewrite': '/_site_foo/styleguide',
|
|
618
|
+
},
|
|
619
|
+
cookies: 'sc_site=foo; HttpOnly; Secure; SameSite=None',
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
expect(siteResolver.getByHost.calledWith('foo.net')).to.be.true;
|
|
623
|
+
|
|
624
|
+
expect(mockNext).calledWith(sinon.match({ pathname: '/_site_foo/styleguide' }));
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
describe('error handling', () => {
|
|
629
|
+
const context = createContext();
|
|
630
|
+
const res = createResponse();
|
|
631
|
+
|
|
632
|
+
let errorSpy: sinon.SinonSpy<[message?: any, ...optionalParams: any[]], void>;
|
|
633
|
+
|
|
634
|
+
before(() => {
|
|
635
|
+
errorSpy = spy(console, 'log');
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
beforeEach(() => {
|
|
639
|
+
errorSpy.resetHistory();
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
after(() => {
|
|
643
|
+
errorSpy.restore();
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it('should handle error', async () => {
|
|
647
|
+
const error = new Error('Custom error');
|
|
648
|
+
|
|
649
|
+
class SampleSiteResolver extends SiteResolver {
|
|
650
|
+
constructor(sites: SiteInfo[]) {
|
|
651
|
+
super(sites);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
getByHost = () => {
|
|
655
|
+
throw error;
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const middleware = new MultisiteMiddleware({ ...defaultConfig });
|
|
660
|
+
middleware['siteResolver'] = new SampleSiteResolver([]);
|
|
661
|
+
|
|
662
|
+
const mockNext = sinon.stub().returns(res);
|
|
663
|
+
|
|
664
|
+
const finalRes = await middleware.handle(context, mockNext);
|
|
665
|
+
|
|
666
|
+
expect(errorSpy.getCall(0).calledWith('Multisite middleware failed:')).to.be.true;
|
|
667
|
+
expect(errorSpy.getCall(1).calledWith(error)).to.be.true;
|
|
668
|
+
|
|
669
|
+
expect(finalRes).to.deep.equal(res);
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
});
|