aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWouter Admiraal <45544358+wouter-admiraal-sonarsource@users.noreply.github.com>2019-08-12 08:39:46 +0200
committerSonarTech <sonartech@sonarsource.com>2019-08-12 20:21:13 +0200
commit21805ac1812e1db25ea1b8267593c6893b367fb4 (patch)
treef0c5c2bd3d8520f9727876f76658095d34c04794
parent984396ddf2e5ad5b5b22fe62408f77462e85b17a (diff)
downloadsonarqube-21805ac1812e1db25ea1b8267593c6893b367fb4.tar.gz
sonarqube-21805ac1812e1db25ea1b8267593c6893b367fb4.zip
SONAR-12325 Limit embedded documentation overrides to language plugins
-rw-r--r--server/sonar-web/src/main/js/apps/documentation/__tests__/pages-test.ts10
-rw-r--r--server/sonar-web/src/main/js/apps/documentation/components/App.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/documentation/pages.ts16
4 files changed, 68 insertions, 32 deletions
diff --git a/server/sonar-web/src/main/js/apps/documentation/__tests__/pages-test.ts b/server/sonar-web/src/main/js/apps/documentation/__tests__/pages-test.ts
index 4bcd5fae2ab..12bf2c890f8 100644
--- a/server/sonar-web/src/main/js/apps/documentation/__tests__/pages-test.ts
+++ b/server/sonar-web/src/main/js/apps/documentation/__tests__/pages-test.ts
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ParsedContent } from '../../../helpers/markdown';
import { mockDocumentationMarkdown } from '../../../helpers/testMocks';
jest.mock('remark', () => ({
@@ -86,10 +87,9 @@ it('should correctly handle overrides (replace & add)', () => {
key: 'tata'
};
- const overrides: string[] = [
- mockDocumentationMarkdown(overrideFooDoc),
- mockDocumentationMarkdown(newDoc)
- ];
+ const overrides: T.Dict<ParsedContent> = {};
+ overrides[foo.url] = { frontmatter: overrideFooDoc, content: overrideFooDoc.content };
+ overrides[`analysis/languages/${newDoc.key}`] = { frontmatter: newDoc, content: newDoc.content };
const pages = getPages(overrides);
expect(pages.length).toBe(3);
@@ -99,7 +99,7 @@ it('should correctly handle overrides (replace & add)', () => {
expect(pages[2].title).toBe(newDoc.title);
});
-function getPages(overrides: string[] = []) {
+function getPages(overrides: T.Dict<ParsedContent> = {}) {
// This allows the use of out-of-scope data inside jest.mock
// Usually, it is impossible as jest.mock'ed module is hoisted on the top of the file
return require.requireActual('../pages').default(overrides);
diff --git a/server/sonar-web/src/main/js/apps/documentation/components/App.tsx b/server/sonar-web/src/main/js/apps/documentation/components/App.tsx
index 1c4a430daf7..15c515ed206 100644
--- a/server/sonar-web/src/main/js/apps/documentation/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/documentation/components/App.tsx
@@ -33,7 +33,9 @@ import A11ySkipTarget from '../../../app/components/a11y/A11ySkipTarget';
import NotFound from '../../../app/components/NotFound';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import DocMarkdownBlock from '../../../components/docs/DocMarkdownBlock';
+import { ParsedContent, separateFrontMatter } from '../../../helpers/markdown';
import { isSonarCloud } from '../../../helpers/system';
+import { getUrlsList } from '../navTreeUtils';
import getPages from '../pages';
import '../styles.css';
import { DocumentationEntry } from '../utils';
@@ -49,6 +51,8 @@ interface State {
tree: DocNavigationItem[];
}
+const LANGUAGES_BASE_URL = 'analysis/languages';
+
export default class App extends React.PureComponent<Props, State> {
mounted = false;
state: State = {
@@ -67,7 +71,7 @@ export default class App extends React.PureComponent<Props, State> {
? ((navigationTreeSonarCloud as any).default as DocNavigationItem[])
: ((navigationTreeSonarQube as any).default as DocNavigationItem[]);
- this.getLanguagesOverrides().then(
+ this.getLanguagePluginsDocumentation(tree).then(
overrides => {
if (this.mounted) {
this.setState({
@@ -92,15 +96,13 @@ export default class App extends React.PureComponent<Props, State> {
removeSideBarClass();
}
- getLanguagesOverrides = () => {
- const pluginStaticFileNameRegEx = new RegExp(`^static/(.*)`);
-
+ getLanguagePluginsDocumentation = (tree: DocNavigationItem[]) => {
return getInstalledPlugins()
.then(plugins =>
Promise.all(
plugins.map(plugin => {
if (plugin.documentationPath) {
- const matchArray = pluginStaticFileNameRegEx.exec(plugin.documentationPath);
+ const matchArray = /^static\/(.*)/.exec(plugin.documentationPath);
if (matchArray && matchArray.length > 1) {
// eslint-disable-next-line promise/no-nesting
@@ -114,7 +116,29 @@ export default class App extends React.PureComponent<Props, State> {
})
)
)
- .then(contents => contents.filter(isDefined));
+ .then(contents => contents.filter(isDefined))
+ .then(contents => {
+ const regex = new RegExp(`/${LANGUAGES_BASE_URL}/\\w+/$`);
+ const overridablePaths = getUrlsList(tree).filter(
+ path => regex.test(path) && path !== `/${LANGUAGES_BASE_URL}/overview/`
+ );
+
+ const parsedContent: T.Dict<ParsedContent> = {};
+
+ contents.forEach(content => {
+ const parsed = separateFrontMatter(content);
+ if (
+ parsed &&
+ parsed.frontmatter &&
+ parsed.frontmatter.key &&
+ overridablePaths.includes(`/${LANGUAGES_BASE_URL}/${parsed.frontmatter.key}/`)
+ ) {
+ parsedContent[`${LANGUAGES_BASE_URL}/${parsed.frontmatter.key}`] = parsed;
+ }
+ });
+
+ return parsedContent;
+ });
};
render() {
diff --git a/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx
index df65c2620ab..da36befcd1b 100644
--- a/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/documentation/components/__tests__/App-test.tsx
@@ -23,6 +23,7 @@ import { addSideBarClass, removeSideBarClass } from 'sonar-ui-common/helpers/pag
import { request } from 'sonar-ui-common/helpers/request';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { isSonarCloud } from '../../../../helpers/system';
+import getPages from '../../pages';
import App from '../App';
jest.mock('../../../../components/common/ScreenPositionHelper', () => ({
@@ -90,19 +91,27 @@ jest.mock('sonar-ui-common/helpers/pages', () => ({
removeSideBarClass: jest.fn()
}));
-jest.mock('sonar-ui-common/helpers/request', () => ({
- request: jest.fn(() => ({
- submit: jest.fn().mockResolvedValue({ status: 200, text: jest.fn().mockReturnValue('TEST') })
- }))
-}));
+jest.mock('sonar-ui-common/helpers/request', () => {
+ const { mockDocumentationMarkdown } = require.requireActual('../../../../helpers/testMocks');
+ return {
+ request: jest.fn(() => ({
+ submit: jest.fn().mockResolvedValue({
+ status: 200,
+ text: jest.fn().mockResolvedValue(mockDocumentationMarkdown({ key: 'csharp' }))
+ })
+ }))
+ };
+});
jest.mock('../../pages', () => {
const { mockDocumentationEntry } = require.requireActual('../../../../helpers/testMocks');
return {
- default: () => [
- mockDocumentationEntry(),
- mockDocumentationEntry({ url: '/analysis/languages/csharp/' })
- ]
+ default: jest
+ .fn()
+ .mockReturnValue([
+ mockDocumentationEntry(),
+ mockDocumentationEntry({ url: '/analysis/languages/csharp/' })
+ ])
};
});
@@ -116,6 +125,10 @@ jest.mock('../../../../api/plugins', () => ({
])
}));
+beforeEach(() => {
+ jest.clearAllMocks();
+});
+
it('should render correctly for SonarQube', async () => {
const wrapper = shallowRender();
expect(wrapper.find('DeferredSpinner').exists()).toBe(true);
@@ -143,12 +156,19 @@ it("should show a 404 if the page doesn't exist", async () => {
});
it('should try to fetch language plugin documentation if documentationPath matches', async () => {
+ (isSonarCloud as jest.Mock).mockReturnValue(false);
+
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
expect(request).toHaveBeenCalledWith('/static/csharp/documentation.md');
expect(request).not.toHaveBeenCalledWith('/static/vbnet/documentation.md');
expect(request).not.toHaveBeenCalledWith('/static/vbnett/documentation.md');
+ expect(getPages).toHaveBeenCalledWith(
+ expect.objectContaining({
+ 'analysis/languages/csharp': expect.any(Object)
+ })
+ );
});
function shallowRender(props: Partial<App['props']> = {}) {
diff --git a/server/sonar-web/src/main/js/apps/documentation/pages.ts b/server/sonar-web/src/main/js/apps/documentation/pages.ts
index 15ded856918..e38a56da341 100644
--- a/server/sonar-web/src/main/js/apps/documentation/pages.ts
+++ b/server/sonar-web/src/main/js/apps/documentation/pages.ts
@@ -23,18 +23,10 @@ import { filterContent, ParsedContent, separateFrontMatter } from '../../helpers
import * as Docs from './documentation.directory-loader';
import { DocumentationEntry, DocumentationEntryScope } from './utils';
-const LANGUAGES_BASE_URL = 'analysis/languages';
-
-export default function getPages(overrides: string[] = []): DocumentationEntry[] {
- const parsedOverrides: T.Dict<ParsedContent> = {};
- overrides.forEach(override => {
- const parsedOverride = separateFrontMatter(override);
- if (parsedOverride && parsedOverride.frontmatter && parsedOverride.frontmatter.key) {
- parsedOverrides[`${LANGUAGES_BASE_URL}/${parsedOverride.frontmatter.key}`] = parsedOverride;
- }
- });
-
- // Merge with existing entries.
+export default function getPages(
+ parsedOverrides: T.Dict<ParsedContent> = {}
+): DocumentationEntry[] {
+ // Get entries, merge with overrides if applicable.
const pages = ((Docs as unknown) as Array<{ content: string; path: string }>).map(file => {
let parsed = separateFrontMatter(file.content);