Skip to content

Commit

Permalink
feat(devfile): Display devfile v2 getting started if some are availab…
Browse files Browse the repository at this point in the history
…le and if devWorkspace setting is enabled

Use the factory resolver to fetch the devfile content as the v2 links are repositories link (not direct devfile.yaml links stored in the devfile registry)

eclipse-che/che#19923
  • Loading branch information
benoitf committed Jun 4, 2021
1 parent 89e9d05 commit 610c6e9
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 12 deletions.
19 changes: 17 additions & 2 deletions src/pages/GetStarted/GetStartedTab/SamplesListGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import * as DevfileRegistriesStore from '../../../store/DevfileRegistries';
import { SampleCard } from './SampleCard';
import { AlertItem } from '../../../services/helpers/types';
import { selectMetadataFiltered } from '../../../store/DevfileRegistries/selectors';
import { selectWorkspacesSettings } from '../../../store/Workspaces/Settings/selectors';
import * as FactoryResolverStore from '../../../store/FactoryResolver';
import stringify from '../../../services/helpers/editor';

type Props =
MappedProps
Expand Down Expand Up @@ -85,8 +88,17 @@ export class SamplesListGallery extends React.PureComponent<Props, State> {

private async fetchDevfile(meta: che.DevfileMetaData): Promise<void> {
try {
const devfile = await this.props.requestDevfile(meta.links.self) as string;
this.props.onCardClick(devfile, meta.displayName);
const cheDevworkspaceEnabled = this.props.workspacesSettings['che.devworkspaces.enabled'] === 'true';
let devfileContent;
if (cheDevworkspaceEnabled) {
const link = meta.links.v2;
await this.props.requestFactoryResolver(link);
const { devfile } = this.props.factoryResolver.resolver;
devfileContent = stringify(devfile);
} else {
devfileContent = await this.props.requestDevfile(meta.links.self) as string;
}
this.props.onCardClick(devfileContent, meta.displayName);
} catch (e) {
console.warn('Failed to load devfile.', e);

Expand Down Expand Up @@ -134,12 +146,15 @@ export class SamplesListGallery extends React.PureComponent<Props, State> {

const mapStateToProps = (state: AppState) => ({
metadataFiltered: selectMetadataFiltered(state),
workspacesSettings: selectWorkspacesSettings(state),
factoryResolver: state.factoryResolver,
});

const connector = connect(
mapStateToProps,
{
...DevfileRegistriesStore.actionCreators,
...FactoryResolverStore.actionCreators,
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,47 @@
*/

import React from 'react';
import { Store } from 'redux';
import { render, screen, RenderResult, fireEvent } from '@testing-library/react';
import { Action, Store } from 'redux';
import { render, screen, RenderResult, fireEvent, waitFor } from '@testing-library/react';
import mockAxios from 'axios';
import SamplesListGallery from '../SamplesListGallery';
import { Provider } from 'react-redux';
import mockMetadata from '../../__tests__/devfileMetadata.json';
import { FakeStoreBuilder } from '../../../../store/__mocks__/storeBuilder';
import { BrandingData } from '../../../../services/bootstrap/branding.constant';
import { WorkspaceSettings } from 'che';
import * as FactoryResolverStore from '../../../../store/FactoryResolver';
import { AppThunk } from '../../../../store';

const requestFactoryResolverMock = jest.fn().mockResolvedValue(undefined);

jest.mock('../../../../store/FactoryResolver', () => {
return {
actionCreators: {
requestFactoryResolver: (location: string, overrideParams?: {
[params: string]: string
}) => async (): Promise<void> => {
if (!overrideParams) {
requestFactoryResolverMock(location);
} else {
requestFactoryResolverMock(location, overrideParams);
}
}
}
};
});

describe('Samples List Gallery', () => {

beforeEach(() => {
jest.useFakeTimers();
});

afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});

function renderGallery(
store: Store,
onCardClicked: () => void = (): void => undefined
Expand All @@ -42,6 +72,17 @@ describe('Samples List Gallery', () => {
expect(cards.length).toEqual(26);
});

it('should render cards with v2 metadata only', () => {
// eslint-disable-next-line
const store = createFakeStoreWithMetadata(true);
renderGallery(store);

const cards = screen.getAllByRole('article');
// only one link is with devfile v2 format
expect(cards.length).toEqual(1);

});

it('should handle "onCardClick" event', async () => {

let resolveFn: {
Expand All @@ -66,6 +107,33 @@ describe('Samples List Gallery', () => {

});

it('should handle "onCardClick" event for v2 metadata', async () => {

let resolveFn: {
(value?: unknown): void;
};
const onCardClickedPromise = new Promise(resolve => resolveFn = resolve);
const onCardClicked = jest.fn(() => resolveFn());

// eslint-disable-next-line
const store = createFakeStoreWithMetadata(true);
renderGallery(store, onCardClicked);

(mockAxios.get as any).mockResolvedValueOnce({
data: {},
});

const cardHeader = screen.getByText('Java with Spring Boot and MySQL');
fireEvent.click(cardHeader);

await onCardClickedPromise;
expect(onCardClicked).toHaveBeenCalled();
jest.runOnlyPendingTimers();
// should have been called with the v2 link
await waitFor(() => expect(requestFactoryResolverMock).toHaveBeenCalledWith('http://my-fake-repository.com/'));

});

it('should render empty state', () => {
// eslint-disable-next-line
const store = createFakeStoreWithoutMetadata();
Expand All @@ -77,19 +145,30 @@ describe('Samples List Gallery', () => {

});

function createFakeStore(metadata?: che.DevfileMetaData[]): Store {
function createFakeStore(metadata?: che.DevfileMetaData[], devWorkspaceEnabled?: boolean): Store {
const registries = {};
if (metadata) {
registries['registry-location'] = {
metadata,
};
}
const workspaceSettings = {};
if (devWorkspaceEnabled) {
workspaceSettings['che.devworkspaces.enabled'] = 'true';
}
return new FakeStoreBuilder()
.withBranding({
docs: {
storageTypes: 'https://docs.location'
}
} as BrandingData)
.withWorkspacesSettings(workspaceSettings as WorkspaceSettings)
.withFactoryResolver({
v: '4.0',
source: 'devfile.yaml',
devfile: {},
location: 'http://fake-location',
})
.withDevfileRegistries({ registries })
.build();
}
Expand All @@ -98,6 +177,6 @@ function createFakeStoreWithoutMetadata(): Store {
return createFakeStore();
}

function createFakeStoreWithMetadata(): Store {
return createFakeStore(mockMetadata);
function createFakeStoreWithMetadata(devWorkspaceEnabled?: boolean): Store {
return createFakeStore(mockMetadata, devWorkspaceEnabled);
}
3 changes: 2 additions & 1 deletion src/pages/GetStarted/__tests__/devfileMetadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@
"icon": "/images/springboot.svg",
"globalMemoryLimit": "3372Mi",
"links": {
"self": "/devfiles/java-mysql/devfile.yaml"
"self": "/devfiles/java-mysql/devfile.yaml",
"v2": "http://my-fake-repository.com/"
}
},
{
Expand Down
20 changes: 16 additions & 4 deletions src/store/DevfileRegistries/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@
import { createSelector } from 'reselect';
import { AppState } from '../';
import match from '../../services/helpers/filter';
import { selectWorkspacesSettingsState } from '../Workspaces/Settings/selectors';

const selectState = (state: AppState) => state.devfileRegistries;

export const selectRegistriesMetadata = createSelector(
selectState,
state => {
const registriesMetadata = Object.values(state.registries)
.map(registryMetadata => registryMetadata.metadata || []);
return mergeRegistriesMetadata(registriesMetadata);
selectWorkspacesSettingsState,
(devfileRegistriesState, workspacesSettingsState) => {
const registriesMetadata = Object.values(devfileRegistriesState.registries).map(registryMetadata => registryMetadata.metadata || []);
const metadata = mergeRegistriesMetadata(registriesMetadata);
const cheDevworkspaceEnabled = workspacesSettingsState.settings['che.devworkspaces.enabled'] === 'true';
if (cheDevworkspaceEnabled) {
return filterDevfileV2Metadata(metadata);
} else {
return metadata;
}

}
);

Expand Down Expand Up @@ -69,6 +77,10 @@ function mergeRegistriesMetadata(registriesMetadata: Array<Array<che.DevfileMeta
}, []);
}

function filterDevfileV2Metadata(metadata: Array<che.DevfileMetaData>): Array<che.DevfileMetaData> {
return metadata.filter(metadata => metadata.links?.v2);
}

export const selectDevfileSchema = createSelector(
selectState,
state => state.schema.schema,
Expand Down

0 comments on commit 610c6e9

Please sign in to comment.