Skip to content

Commit

Permalink
Check if given path exists for core/client openAsX functions (SAP#2298)
Browse files Browse the repository at this point in the history
  • Loading branch information
ndricimrr authored Nov 2, 2021
1 parent 25cf3ec commit 94c36e5
Show file tree
Hide file tree
Showing 11 changed files with 264 additions and 33 deletions.
27 changes: 27 additions & 0 deletions core/src/App.html
Original file line number Diff line number Diff line change
Expand Up @@ -1353,17 +1353,44 @@
} else if (e.data.params.modal !== undefined) {
let path = buildPath(e.data.params, srcNode, srcPathParams);
path = GenericHelpers.addLeadingSlash(path);
const pathExist = await pathExists(path);
path = await RoutingHelpers.handlePageNotFoundAndRetrieveRedirectPath(
getComponentWrapper(),
path,
pathExist
);
if (!path) {
return;
}
contentNode = node;
resetMicrofrontendModalData();
openViewInModal(path, e.data.params.modal === true ? {} : e.data.params.modal);
} else if (e.data.params.splitView !== undefined) {
let path = buildPath(e.data.params, srcNode, srcPathParams);
path = GenericHelpers.addLeadingSlash(path);
const pathExist = await pathExists(path);
path = await RoutingHelpers.handlePageNotFoundAndRetrieveRedirectPath(
getComponentWrapper(),
path,
pathExist
);
if (!path) {
return;
}
contentNode = node;
openSplitView(path, e.data.params.splitView);
} else if (e.data.params.drawer !== undefined) {
let path = buildPath(e.data.params, srcNode, srcPathParams);
path = GenericHelpers.addLeadingSlash(path);
const pathExist = await pathExists(path);
path = await RoutingHelpers.handlePageNotFoundAndRetrieveRedirectPath(
getComponentWrapper(),
path,
pathExist
);
if (!path) {
return;
}
contentNode = node;
resetMicrofrontendDrawerData();
e.data.params.drawer.isDrawer = true;
Expand Down
5 changes: 5 additions & 0 deletions core/src/core-api/_internalLinkManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export class linkManager extends LuigiCoreAPIBase {
this.navigate(path, true, undefined, undefined, drawerSettings);
}

openAsSplitView(path, splitViewSettings = {}) {
this.navigate(path, true, undefined, splitViewSettings);
return Luigi.splitView.splitViewHandle;
}

fromContext(navigationContext) {
this.options.fromContext = navigationContext;
return this;
Expand Down
4 changes: 1 addition & 3 deletions core/src/core-api/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ class LuigiNavigationManager {
console.warn('Navigation with an absolute path prevented.');
return;
}

Luigi.splitView.openAsSplitView(path, splitViewSettings);
return Luigi.splitView.splitViewHandle;
return new linkManager().openAsSplitView(path, splitViewSettings);
}

/**
Expand Down
1 change: 0 additions & 1 deletion core/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ const configReadyCallback = () => {
};

Luigi.splitView = {
openAsSplitView: (path, settings) => app.$$.ctx.openSplitView(path, settings),
splitViewHandle: {
close: () => app.$$.ctx.closeSplitView(),
collapse: () => app.$$.ctx.collapseSplitView(),
Expand Down
20 changes: 4 additions & 16 deletions core/src/services/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,25 +469,13 @@ class RoutingClass {
}

async showPageNotFoundError(component, pathToRedirect, notFoundPath, isAnyPathMatched = false) {
const pageNotFoundHandler = LuigiConfig.getConfigValue('routing.pageNotFoundHandler');
const redirectPathFromNotFoundHandler = RoutingHelpers.getPageNotFoundRedirectPath(notFoundPath, isAnyPathMatched);

if (typeof pageNotFoundHandler === 'function') {
//custom 404 handler is provided, use it
const result = pageNotFoundHandler(notFoundPath, isAnyPathMatched);
if (result && result.redirectTo) {
this.navigateTo(result.redirectTo);
}
if (redirectPathFromNotFoundHandler) {
this.navigateTo(redirectPathFromNotFoundHandler);
return;
}

const alertSettings = {
text: LuigiI18N.getTranslation(isAnyPathMatched ? 'luigi.notExactTargetNode' : 'luigi.requestedRouteNotFound', {
route: notFoundPath
}),
type: 'error',
ttl: 1 //how many redirections the alert will 'survive'.
};
component.showAlert(alertSettings, false);
RoutingHelpers.showRouteNotFoundAlert(component, notFoundPath, isAnyPathMatched);
this.navigateTo(GenericHelpers.addLeadingSlash(pathToRedirect));
}

Expand Down
59 changes: 59 additions & 0 deletions core/src/utilities/helpers/routing-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,65 @@ class RoutingHelpersClass {
hasIntent(path) {
return !!path && path.toLowerCase().includes('#?intent=');
}

/**
* Queries the pageNotFoundHandler configuration and returns redirect path if it exists
* If the there is no `pageNotFoundHandler` defined we return undefined.
* @param {*} notFoundPath the path to check
* @returns redirect path if it exists, else return undefined
*/
getPageNotFoundRedirectPath(notFoundPath, isAnyPathMatched = false) {
const pageNotFoundHandler = LuigiConfig.getConfigValue('routing.pageNotFoundHandler');
if (typeof pageNotFoundHandler === 'function') {
//custom 404 handler is provided, use it
const result = pageNotFoundHandler(notFoundPath, isAnyPathMatched);
if (result && result.redirectTo) {
return result.redirectTo;
}
}
return undefined;
}

/**
* Handles pageNotFound situation depending if path exists or not.
* If path exists simply return the given path, else fetch the pageNotFound redirect path and return it.
* In case there was no pageNotFound handler defined it shows an alert and returns undefined.
* @param {any} component the component to show the alert on
* @param {string} path the path to check for
* @param {boolean} pathExists defines if path exists or not
* @returns the path to redirect to or undefined if path doesn't exist and no redirect path is defined
*/
async handlePageNotFoundAndRetrieveRedirectPath(component, path, pathExists) {
if (pathExists) {
return path;
}
const redirectPath = this.getPageNotFoundRedirectPath(path);
if (redirectPath !== undefined) {
return redirectPath;
} else {
// default behavior if `pageNotFoundHandler` did not produce a redirect path
this.showRouteNotFoundAlert(component, path);
console.warn(`Could not find the requested route: ${path}`);
return undefined;
}
}

/**
* Shows an alert on the given component given the path
* @param {*} component the component used to call the alert function upon
* @param {string} path the path to show in the alert
* @param {boolean} isAnyPathMatched shows whether a valid path was found / which means path was only partially wrong. Otherwise it is false.
*/
showRouteNotFoundAlert(component, path, isAnyPathMatched = false) {
const alertSettings = {
text: LuigiI18N.getTranslation(isAnyPathMatched ? 'luigi.notExactTargetNode' : 'luigi.requestedRouteNotFound', {
route: path
}),
type: 'error',
ttl: 1 //how many redirections the alert will 'survive'.
};
component.showAlert(alertSettings, false);
}
}

export const RoutingHelpers = new RoutingHelpersClass();
5 changes: 3 additions & 2 deletions core/test/services/routing.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -924,14 +924,15 @@ describe('Routing', function() {
let notFoundPath = '/this/does/not/exist';
beforeEach(() => {
sinon.stub(Routing, 'navigateTo');
sinon.stub(RoutingHelpers, 'showRouteNotFoundAlert');
sinon.stub(LuigiI18N, 'getTranslation');
sinon.stub(component, 'showAlert');
});

it('navigate to redirect path', () => {
it('navigate to redirect path', async () => {
LuigiConfig.getConfigValue.returns(null);

Routing.showPageNotFoundError(component, pathToRedirect, notFoundPath);
await Routing.showPageNotFoundError(component, pathToRedirect, notFoundPath);

sinon.assert.calledWithExactly(Routing.navigateTo, pathToRedirect);
});
Expand Down
125 changes: 125 additions & 0 deletions core/test/utilities/helpers/routing-helpers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -696,4 +696,129 @@ describe('Routing-helpers', () => {
sinon.assert.calledWith(console.warn, 'No permission to add "luigi" to the url');
});
});

describe('hasIntent', () => {
it('checks against correct intent keyword', () => {
const path = '#?intent=';
const hasIntent = RoutingHelpers.hasIntent(path);
assert.isTrue(hasIntent);
});

it('check against incorrect intent keyword', () => {
const path = '#?int=';
const hasIntent = RoutingHelpers.hasIntent(path);
assert.isFalse(hasIntent);
});

it('check against undefined intent keyword', () => {
const path = undefined;
const hasIntent = RoutingHelpers.hasIntent(path);
assert.isFalse(hasIntent);
});
});

describe('hasIntent', () => {
it('checks against correct intent keyword', () => {
const path = '#?intent=';
const hasIntent = RoutingHelpers.hasIntent(path);
assert.isTrue(hasIntent);
});

it('check against incorrect intent keyword', () => {
const path = '#?int=';
const hasIntent = RoutingHelpers.hasIntent(path);
assert.isFalse(hasIntent);
});

it('check against undefined intent keyword', () => {
const path = undefined;
const hasIntent = RoutingHelpers.hasIntent(path);
assert.isFalse(hasIntent);
});
});

describe('getPageNotFoundRedirectPath', () => {
afterEach(() => {
sinon.restore();
sinon.reset();
});

it('with custom pageNotFoundHandler defined', async () => {
const customRedirect = 'somecustompath';
sinon
.stub(LuigiConfig, 'getConfigValue')
.withArgs('routing.pageNotFoundHandler')
.returns(() => {
return { redirectTo: customRedirect };
});
const expected = await RoutingHelpers.getPageNotFoundRedirectPath('notFoundPath');
assert.equal(customRedirect, expected);
});

it('with custom pageNotFoundHandler not defined', async () => {
sinon
.stub(LuigiConfig, 'getConfigValue')
.withArgs('routing.pageNotFoundHandler')
.returns(undefined);
const expected = await RoutingHelpers.getPageNotFoundRedirectPath('notFoundPath');
assert.equal(undefined, expected);
});

it('with custom pageNotFoundHandler not a function', async () => {
sinon
.stub(LuigiConfig, 'getConfigValue')
.withArgs('routing.pageNotFoundHandler')
.returns({ thisObject: 'should be function instead' });
const expected = await RoutingHelpers.getPageNotFoundRedirectPath('notFoundPath');
assert.equal(undefined, expected);
});
});

describe('handlePageNotFoundAndRetrieveRedirectPath', () => {
const component = {
showAlert: () => {}
};

beforeEach(() => {
console.warn = sinon.spy();
sinon.stub(LuigiI18N, 'getTranslation');
sinon.stub(RoutingHelpers, 'getPageNotFoundRedirectPath');
sinon.stub(RoutingHelpers, 'showRouteNotFoundAlert');
sinon.stub(component, 'showAlert');
});

afterEach(() => {
sinon.restore();
sinon.reset();
});

it('when path exists should return path itself', async () => {
const path = 'existingpath';
const expected = await RoutingHelpers.handlePageNotFoundAndRetrieveRedirectPath(component, path, true);
assert.equal(path, expected);
});

it('with custom pageNotFoundHandler defined', async () => {
const redirectPath = 'somepathtoredirect';
// define pageNotFoundHandler return value with stub
RoutingHelpers.getPageNotFoundRedirectPath.returns(redirectPath);
// call function being tested
const expected = await RoutingHelpers.handlePageNotFoundAndRetrieveRedirectPath(component, redirectPath, false);
assert.equal(redirectPath, expected);
});

it('with custom pageNotFoundHandler as not defined', async () => {
const path = 'notFoundPath';
LuigiI18N.getTranslation
.withArgs('luigi.requestedRouteNotFound', { route: path })
.returns('Could not find the requested route');
// set pageNotFoundHandler as undefined with stub
RoutingHelpers.getPageNotFoundRedirectPath.returns(undefined);
// call function being tested
const expected = await RoutingHelpers.handlePageNotFoundAndRetrieveRedirectPath(component, path, false);
sinon.assert.calledWith(console.warn, `Could not find the requested route: ${path}`);
sinon.assert.calledOnce(RoutingHelpers.showRouteNotFoundAlert);
assert.equal(undefined, expected);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,11 @@ describe('Luigi client linkManager', () => {
.invoke('width')
.should('eq', containerWidth);

win.Luigi.navigation().openAsDrawer('/projects/pr1/drawer', { overlap: false });
cy.wrap($iframeBody)
.contains('Open drawer with no overlap')
.click();

cy.wait(500);

cy.get('.drawer-dialog')
.invoke('width')
Expand Down
32 changes: 22 additions & 10 deletions test/e2e-test-application/e2e/tests/1-angular/navigation.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,26 +90,36 @@ describe('Navigation', () => {
}, 50);
});
});
it('Core API collapse in SplitView', () => {
it('Core API collapse in SplitView', done => {
cy.window().then(win => {
const handle = win.Luigi.navigation().openAsSplitView('/ext', {
title: 'Preserved Split View',
size: '40',
collapsed: false
});
handle.collapse();
cy.expect(handle.isCollapsed()).to.be.true;
setTimeout(() => {
handle.collapse();
setTimeout(() => {
cy.expect(handle.isCollapsed()).to.be.true;
done();
}, 50);
}, 50);
});
});
it('Core API expand SplitView', () => {
it('Core API expand SplitView', done => {
cy.window().then(win => {
const handle = win.Luigi.navigation().openAsSplitView('/ext', {
title: 'Preserved Split View',
size: '40',
collapsed: false
});
handle.expand();
cy.expect(handle.isExpanded()).to.be.true;
setTimeout(() => {
handle.expand();
setTimeout(() => {
cy.expect(handle.isExpanded()).to.be.true;
done();
}, 50);
}, 50);
});
});
it('Core API open collapsed splitview and check if expand container will disappear after navigation', () => {
Expand All @@ -120,12 +130,14 @@ describe('Navigation', () => {
size: '40',
collapsed: true
});
cy.get('#splitViewContainer').should('be.visible');
cy.get('.fd-shellbar')
setTimeout(() => {
cy.get('#splitViewContainer').should('be.visible');
cy.get('.fd-shellbar')
.contains('Projects')
.click();
cy.expectPathToBe('/projects');
cy.get('#splitViewContainer').should('not.be.visible');
cy.expectPathToBe('/projects');
cy.get('#splitViewContainer').should('not.be.visible');
}, 0);
});
});
it('Core API navigate with params', () => {
Expand Down
Loading

0 comments on commit 94c36e5

Please sign in to comment.