mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-07 21:31:48 +08:00
Merge pull request #11215 from storybookjs/fix/refs-rename-authUrl
Fix/refs rename auth url & add tests for modules/refs
This commit is contained in:
commit
194fdd5d60
@ -77,11 +77,11 @@ For an example what this file should look like, see: [here](https://next--storyb
|
|||||||
If you have some authentication layer on your hosted storybook, the composing the storybook will fail. Storybook will show a message in the sidebar if that happens.
|
If you have some authentication layer on your hosted storybook, the composing the storybook will fail. Storybook will show a message in the sidebar if that happens.
|
||||||
|
|
||||||
|
|
||||||
You can assist the user by creating a `metadata.json` file with a `authUrl` field, and ensure this file **is** loadable (even in the user is not authenticated):
|
You can assist the user by creating a `metadata.json` file with a `loginUrl` field, and ensure this file **is** loadable (even in the user is not authenticated):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"authUrl": "https://example.com"
|
"loginUrl": "https://example.com"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -15,9 +15,11 @@ export interface SubState {
|
|||||||
|
|
||||||
type Versions = Record<string, string>;
|
type Versions = Record<string, string>;
|
||||||
|
|
||||||
export type SetRefData = Omit<ComposedRef, 'stories'> & {
|
export type SetRefData = Partial<
|
||||||
stories?: StoriesRaw;
|
Omit<ComposedRef, 'stories'> & {
|
||||||
};
|
stories?: StoriesRaw;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
export interface SubAPI {
|
export interface SubAPI {
|
||||||
findRef: (source: string) => ComposedRef;
|
findRef: (source: string) => ComposedRef;
|
||||||
@ -36,7 +38,7 @@ export interface ComposedRef {
|
|||||||
type?: 'auto-inject' | 'unknown' | 'lazy';
|
type?: 'auto-inject' | 'unknown' | 'lazy';
|
||||||
stories: StoriesHash;
|
stories: StoriesHash;
|
||||||
versions?: Versions;
|
versions?: Versions;
|
||||||
authUrl?: string;
|
loginUrl?: string;
|
||||||
ready?: boolean;
|
ready?: boolean;
|
||||||
error?: any;
|
error?: any;
|
||||||
}
|
}
|
||||||
@ -48,12 +50,12 @@ export type RefUrl = string;
|
|||||||
// eslint-disable-next-line no-useless-escape
|
// eslint-disable-next-line no-useless-escape
|
||||||
const findFilename = /(\/((?:[^\/]+?)\.[^\/]+?)|\/)$/;
|
const findFilename = /(\/((?:[^\/]+?)\.[^\/]+?)|\/)$/;
|
||||||
|
|
||||||
const allSettled = (promises: Promise<any>[]) =>
|
const allSettled = (promises: Promise<Response>[]): Promise<(Response | false)[]> =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
promises.map((promise, i) =>
|
promises.map((promise) =>
|
||||||
promise.then(
|
promise.then(
|
||||||
(r) => (r.ok ? r : false),
|
(r) => (r.ok ? r : (false as const)),
|
||||||
() => false
|
() => false as const
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -98,7 +100,7 @@ const map = (
|
|||||||
return input;
|
return input;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const init: ModuleFn = ({ store, provider, fullAPI }) => {
|
export const init: ModuleFn = ({ store, provider, fullAPI }, { runCheck = true } = {}) => {
|
||||||
const api: SubAPI = {
|
const api: SubAPI = {
|
||||||
findRef: (source) => {
|
findRef: (source) => {
|
||||||
const refs = api.getRefs();
|
const refs = api.getRefs();
|
||||||
@ -123,7 +125,7 @@ export const init: ModuleFn = ({ store, provider, fullAPI }) => {
|
|||||||
checkRef: async (ref) => {
|
checkRef: async (ref) => {
|
||||||
const { id, url } = ref;
|
const { id, url } = ref;
|
||||||
|
|
||||||
const loadedData: { error?: Error; stories?: StoriesRaw } = {};
|
const loadedData: { error?: Error; stories?: StoriesRaw; loginUrl?: string } = {};
|
||||||
|
|
||||||
const [included, omitted, iframe] = await allSettled([
|
const [included, omitted, iframe] = await allSettled([
|
||||||
fetch(`${url}/stories.json`, {
|
fetch(`${url}/stories.json`, {
|
||||||
@ -144,7 +146,7 @@ export const init: ModuleFn = ({ store, provider, fullAPI }) => {
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handle = async (request: Promise<Response> | false) => {
|
const handle = async (request: Response | false): Promise<SetRefData> => {
|
||||||
if (request) {
|
if (request) {
|
||||||
return Promise.resolve(request)
|
return Promise.resolve(request)
|
||||||
.then((response) => (response.ok ? response.json() : {}))
|
.then((response) => (response.ok ? response.json() : {}))
|
||||||
@ -168,10 +170,10 @@ export const init: ModuleFn = ({ store, provider, fullAPI }) => {
|
|||||||
`,
|
`,
|
||||||
} as Error;
|
} as Error;
|
||||||
} else if (omitted || included) {
|
} else if (omitted || included) {
|
||||||
const credentials = !omitted ? 'include' : 'omit';
|
const credentials = included ? 'include' : 'omit';
|
||||||
|
|
||||||
const [stories, metadata] = await Promise.all([
|
const [stories, metadata] = await Promise.all([
|
||||||
handle(omitted || included),
|
included ? handle(included) : handle(omitted),
|
||||||
handle(
|
handle(
|
||||||
fetch(`${url}/metadata.json`, {
|
fetch(`${url}/metadata.json`, {
|
||||||
headers: {
|
headers: {
|
||||||
@ -179,14 +181,14 @@ export const init: ModuleFn = ({ store, provider, fullAPI }) => {
|
|||||||
},
|
},
|
||||||
credentials,
|
credentials,
|
||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
})
|
}).catch(() => false)
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Object.assign(loadedData, { ...stories, ...metadata });
|
Object.assign(loadedData, { ...stories, ...metadata });
|
||||||
}
|
}
|
||||||
|
|
||||||
api.setRef(id, {
|
await api.setRef(id, {
|
||||||
id,
|
id,
|
||||||
url,
|
url,
|
||||||
...loadedData,
|
...loadedData,
|
||||||
@ -230,9 +232,11 @@ export const init: ModuleFn = ({ store, provider, fullAPI }) => {
|
|||||||
r.type = 'unknown';
|
r.type = 'unknown';
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.entries(refs).forEach(([k, v]) => {
|
if (runCheck) {
|
||||||
api.checkRef(v as SetRefData);
|
Object.entries(refs).forEach(([k, v]) => {
|
||||||
});
|
api.checkRef(v as SetRefData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
api,
|
api,
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"authUrl": "https://example.com"
|
"loginUrl": "https://example.com"
|
||||||
}
|
}
|
@ -1,7 +1,10 @@
|
|||||||
import { getSourceType } from '../modules/refs';
|
import { fetch } from 'global';
|
||||||
|
import { getSourceType, init as initRefs } from '../modules/refs';
|
||||||
|
|
||||||
jest.mock('global', () => {
|
jest.mock('global', () => {
|
||||||
const globalMock = {};
|
const globalMock = {
|
||||||
|
fetch: jest.fn(() => Promise.resolve({})),
|
||||||
|
};
|
||||||
// Change global.location value to handle edge cases
|
// Change global.location value to handle edge cases
|
||||||
// Add additional variations of global.location mock return values in this array.
|
// Add additional variations of global.location mock return values in this array.
|
||||||
// NOTE: The order must match the order that global.location is called in the unit tests.
|
// NOTE: The order must match the order that global.location is called in the unit tests.
|
||||||
@ -20,7 +23,80 @@ jest.mock('global', () => {
|
|||||||
return globalMock;
|
return globalMock;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('refs', () => {
|
const provider = {
|
||||||
|
getConfig: () => ({
|
||||||
|
refs: {
|
||||||
|
fake: {
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = {
|
||||||
|
getState: () => ({
|
||||||
|
refs: {
|
||||||
|
fake: {
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
setState: jest.fn(() => {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const emptyResponse = Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const setupResponses = (
|
||||||
|
a = emptyResponse,
|
||||||
|
b = emptyResponse,
|
||||||
|
c = emptyResponse,
|
||||||
|
d = emptyResponse
|
||||||
|
) => {
|
||||||
|
fetch.mockClear();
|
||||||
|
store.setState.mockClear();
|
||||||
|
|
||||||
|
fetch.mockImplementation((l, o) => {
|
||||||
|
if (l.includes('stories') && o.credentials === 'omit') {
|
||||||
|
return Promise.resolve({
|
||||||
|
ok: a.ok,
|
||||||
|
json: a.response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (l.includes('stories') && o.credentials === 'include') {
|
||||||
|
return Promise.resolve({
|
||||||
|
ok: b.ok,
|
||||||
|
json: b.response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (l.includes('iframe')) {
|
||||||
|
return Promise.resolve({
|
||||||
|
ok: c.ok,
|
||||||
|
json: c.response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (l.includes('metadata')) {
|
||||||
|
return Promise.resolve({
|
||||||
|
ok: d.ok,
|
||||||
|
json: d.response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
ok: false,
|
||||||
|
json: () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Refs API', () => {
|
||||||
describe('getSourceType(source)', () => {
|
describe('getSourceType(source)', () => {
|
||||||
// These tests must be run first and in correct order.
|
// These tests must be run first and in correct order.
|
||||||
// The order matches the "edgecaseLocations" order in the 'global' mock function above.
|
// The order matches the "edgecaseLocations" order in the 'global' mock function above.
|
||||||
@ -53,4 +129,490 @@ describe('refs', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('checkRef', () => {
|
||||||
|
it('on initialization it checks refs', async () => {
|
||||||
|
// given
|
||||||
|
initRefs({ provider, store });
|
||||||
|
|
||||||
|
expect(fetch.mock.calls).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/iframe.html",
|
||||||
|
Object {
|
||||||
|
"cors": "no-cors",
|
||||||
|
"credentials": "omit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks refs (all fail)', async () => {
|
||||||
|
// given
|
||||||
|
const { api } = initRefs({ provider, store }, { runCheck: false });
|
||||||
|
|
||||||
|
setupResponses(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('Failed to fetch');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('Failed to fetch');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await api.checkRef({
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch.mock.calls).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/iframe.html",
|
||||||
|
Object {
|
||||||
|
"cors": "no-cors",
|
||||||
|
"credentials": "omit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(store.setState.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"refs": Object {
|
||||||
|
"fake": Object {
|
||||||
|
"error": Object {
|
||||||
|
"message": "Error: Loading of ref failed
|
||||||
|
at fetch (lib/api/src/modules/refs.ts)
|
||||||
|
|
||||||
|
URL: https://example.com
|
||||||
|
|
||||||
|
We weren't able to load the above URL,
|
||||||
|
it's possible a CORS error happened.
|
||||||
|
|
||||||
|
Please check your dev-tools network tab.",
|
||||||
|
},
|
||||||
|
"id": "fake",
|
||||||
|
"ready": false,
|
||||||
|
"stories": undefined,
|
||||||
|
"title": "Fake",
|
||||||
|
"type": "auto-inject",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks refs (success)', async () => {
|
||||||
|
// given
|
||||||
|
const { api } = initRefs({ provider, store }, { runCheck: false });
|
||||||
|
|
||||||
|
setupResponses(
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({ stories: {} }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({ stories: {} }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({
|
||||||
|
versions: {},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await api.checkRef({
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch.mock.calls).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/iframe.html",
|
||||||
|
Object {
|
||||||
|
"cors": "no-cors",
|
||||||
|
"credentials": "omit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/metadata.json",
|
||||||
|
Object {
|
||||||
|
"cache": "no-cache",
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(store.setState.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"refs": Object {
|
||||||
|
"fake": Object {
|
||||||
|
"id": "fake",
|
||||||
|
"ready": false,
|
||||||
|
"stories": Object {},
|
||||||
|
"title": "Fake",
|
||||||
|
"type": "lazy",
|
||||||
|
"url": "https://example.com",
|
||||||
|
"versions": Object {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks refs (auth)', async () => {
|
||||||
|
// given
|
||||||
|
const { api } = initRefs({ provider, store }, { runCheck: false });
|
||||||
|
|
||||||
|
setupResponses(
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({ loginUrl: 'https://example.com/login' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await api.checkRef({
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch.mock.calls).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/iframe.html",
|
||||||
|
Object {
|
||||||
|
"cors": "no-cors",
|
||||||
|
"credentials": "omit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/metadata.json",
|
||||||
|
Object {
|
||||||
|
"cache": "no-cache",
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(store.setState.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"refs": Object {
|
||||||
|
"fake": Object {
|
||||||
|
"id": "fake",
|
||||||
|
"loginUrl": "https://example.com/login",
|
||||||
|
"ready": false,
|
||||||
|
"stories": undefined,
|
||||||
|
"title": "Fake",
|
||||||
|
"type": "auto-inject",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks refs (mixed)', async () => {
|
||||||
|
// given
|
||||||
|
const { api } = initRefs({ provider, store }, { runCheck: false });
|
||||||
|
|
||||||
|
fetch.mockClear();
|
||||||
|
store.setState.mockClear();
|
||||||
|
|
||||||
|
setupResponses(
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({ loginUrl: 'https://example.com/login' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({ stories: {} }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => ({
|
||||||
|
versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com' },
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await api.checkRef({
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch.mock.calls).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/iframe.html",
|
||||||
|
Object {
|
||||||
|
"cors": "no-cors",
|
||||||
|
"credentials": "omit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/metadata.json",
|
||||||
|
Object {
|
||||||
|
"cache": "no-cache",
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(store.setState.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"refs": Object {
|
||||||
|
"fake": Object {
|
||||||
|
"id": "fake",
|
||||||
|
"ready": false,
|
||||||
|
"stories": Object {},
|
||||||
|
"title": "Fake",
|
||||||
|
"type": "lazy",
|
||||||
|
"url": "https://example.com",
|
||||||
|
"versions": Object {
|
||||||
|
"1.0.0": "https://example.com/v1",
|
||||||
|
"2.0.0": "https://example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks refs (cors)', async () => {
|
||||||
|
// given
|
||||||
|
const { api } = initRefs({ provider, store }, { runCheck: false });
|
||||||
|
|
||||||
|
setupResponses(
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('Failed to fetch');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('Failed to fetch');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: true,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('not ok');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ok: false,
|
||||||
|
response: async () => {
|
||||||
|
throw new Error('Failed to fetch');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await api.checkRef({
|
||||||
|
id: 'fake',
|
||||||
|
url: 'https://example.com',
|
||||||
|
title: 'Fake',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(fetch.mock.calls).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "include",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/stories.json",
|
||||||
|
Object {
|
||||||
|
"credentials": "omit",
|
||||||
|
"headers": Object {
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Array [
|
||||||
|
"https://example.com/iframe.html",
|
||||||
|
Object {
|
||||||
|
"cors": "no-cors",
|
||||||
|
"credentials": "omit",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
|
||||||
|
expect(store.setState.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"refs": Object {
|
||||||
|
"fake": Object {
|
||||||
|
"id": "fake",
|
||||||
|
"ready": false,
|
||||||
|
"stories": undefined,
|
||||||
|
"title": "Fake",
|
||||||
|
"type": "auto-inject",
|
||||||
|
"url": "https://example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1 +1 @@
|
|||||||
export const version = '6.0.0-beta.31';
|
export const version = '6.0.0-beta.31';
|
||||||
|
@ -160,7 +160,10 @@ const ErrorFormatter: FunctionComponent<{ error: Error }> = ({ error }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AuthBlock: FunctionComponent<{ authUrl: string; id: string }> = ({ authUrl, id }) => {
|
export const AuthBlock: FunctionComponent<{ loginUrl: string; id: string }> = ({
|
||||||
|
loginUrl,
|
||||||
|
id,
|
||||||
|
}) => {
|
||||||
const [isAuthAttempted, setAuthAttempted] = useState(false);
|
const [isAuthAttempted, setAuthAttempted] = useState(false);
|
||||||
|
|
||||||
const refresh = useCallback(() => {
|
const refresh = useCallback(() => {
|
||||||
@ -169,12 +172,12 @@ export const AuthBlock: FunctionComponent<{ authUrl: string; id: string }> = ({
|
|||||||
|
|
||||||
const open = useCallback((e) => {
|
const open = useCallback((e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const childWindow = window.open(authUrl, `storybook_auth_${id}`, 'resizable,scrollbars');
|
const childWindow = window.open(loginUrl, `storybook_auth_${id}`, 'resizable,scrollbars');
|
||||||
|
|
||||||
// poll for window to close
|
// poll for window to close
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
if (!childWindow) {
|
if (!childWindow) {
|
||||||
logger.error('unable to access authUrl window');
|
logger.error('unable to access loginUrl window');
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
} else if (childWindow.closed) {
|
} else if (childWindow.closed) {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
@ -189,8 +192,8 @@ export const AuthBlock: FunctionComponent<{ authUrl: string; id: string }> = ({
|
|||||||
{isAuthAttempted ? (
|
{isAuthAttempted ? (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<Text>
|
<Text>
|
||||||
Authentication on <strong>{authUrl}</strong> seems to have concluded, refresh the page
|
Authentication on <strong>{loginUrl}</strong> seems to have concluded, refresh the
|
||||||
to fetch this storybook
|
page to fetch this storybook
|
||||||
</Text>
|
</Text>
|
||||||
<div>
|
<div>
|
||||||
<Button small gray onClick={refresh}>
|
<Button small gray onClick={refresh}>
|
||||||
|
@ -54,7 +54,7 @@ export const Ref: FunctionComponent<RefType & RefProps> = (ref) => {
|
|||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
const indicatorRef = useRef<HTMLElement>(null);
|
const indicatorRef = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
const { stories, id: key, title = key, storyId, filter, isHidden = false, authUrl, error } = ref;
|
const { stories, id: key, title = key, storyId, filter, isHidden = false, loginUrl, error } = ref;
|
||||||
const { dataSet, expandedSet, length, others, roots, setExpanded, selectedSet } = useDataset(
|
const { dataSet, expandedSet, length, others, roots, setExpanded, selectedSet } = useDataset(
|
||||||
stories,
|
stories,
|
||||||
filter,
|
filter,
|
||||||
@ -77,7 +77,7 @@ export const Ref: FunctionComponent<RefType & RefProps> = (ref) => {
|
|||||||
const isLoading = isLoadingMain || isLoadingInjected || ref.type === 'unknown';
|
const isLoading = isLoadingMain || isLoadingInjected || ref.type === 'unknown';
|
||||||
const isError = !!error;
|
const isError = !!error;
|
||||||
const isEmpty = !isLoading && length === 0;
|
const isEmpty = !isLoading && length === 0;
|
||||||
const isAuthRequired = !!authUrl;
|
const isAuthRequired = !!loginUrl && length === 0;
|
||||||
|
|
||||||
const state = getStateType(isLoading, isAuthRequired, isError, isEmpty);
|
const state = getStateType(isLoading, isAuthRequired, isError, isEmpty);
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ export const Ref: FunctionComponent<RefType & RefProps> = (ref) => {
|
|||||||
)}
|
)}
|
||||||
{isExpanded && (
|
{isExpanded && (
|
||||||
<Wrapper data-title={title} isMain={isMain}>
|
<Wrapper data-title={title} isMain={isMain}>
|
||||||
{state === 'auth' && <AuthBlock id={ref.id} authUrl={authUrl} />}
|
{state === 'auth' && <AuthBlock id={ref.id} loginUrl={loginUrl} />}
|
||||||
{state === 'error' && <ErrorBlock error={error} />}
|
{state === 'error' && <ErrorBlock error={error} />}
|
||||||
{state === 'loading' && <LoaderBlock isMain={isMain} />}
|
{state === 'loading' && <LoaderBlock isMain={isMain} />}
|
||||||
{state === 'empty' && <EmptyBlock isMain={isMain} />}
|
{state === 'empty' && <EmptyBlock isMain={isMain} />}
|
||||||
|
@ -88,7 +88,7 @@ const refs: Record<string, RefType> = {
|
|||||||
url: 'https://example.com',
|
url: 'https://example.com',
|
||||||
type: 'lazy',
|
type: 'lazy',
|
||||||
stories: {},
|
stories: {},
|
||||||
authUrl: 'https://example.com',
|
loginUrl: 'https://example.com',
|
||||||
},
|
},
|
||||||
long: {
|
long: {
|
||||||
id: 'long',
|
id: 'long',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user