Commit ddebca08 authored by Lauri Himanen's avatar Lauri Himanen
Browse files

Updated tests to be in line with changes in v0.11.0.

parent caab70db
Pipeline #99985 passed with stages
in 38 minutes and 55 seconds
......@@ -31,7 +31,6 @@ import Quantity from '../Quantity'
import { RecoilRoot } from 'recoil'
import { Link as RouterLink } from 'react-router-dom'
import { DOI } from '../search/DatasetList'
import { domainData } from '../domainData'
import { errorContext } from '../errors'
import { authorList, convertSI, getHighestOccupiedEnergy } from '../../utils'
import { resolveRef, refPath } from '../archive/metainfo'
......@@ -187,7 +186,6 @@ export default function DFTEntryOverview({data}) {
useEffect(() => {
apiv1.results(data.entry_id).then(archive => {
}).catch(error => {
setLoading(false)
if (error.name === 'DoesNotExist') {
} else {
raiseError(error)
......@@ -401,7 +399,6 @@ export default function DFTEntryOverview({data}) {
}, [data, api, raiseError])
const quantityProps = {data: data, loading: !data}
const domain = data.domain && domainData[data.domain]
return (
<RecoilRoot>
......@@ -413,7 +410,7 @@ export default function DFTEntryOverview({data}) {
<Quantity flex>
<Quantity quantity="results.method.simulation.program_name" label='program name' noWrap {...quantityProps}/>
<Quantity quantity="results.method.simulation.program_version" label='program version' ellipsisFront {...quantityProps}/>
<Quantity quantity="results.method.method_name" label='electronic structure method' noWrap {...quantityProps}/>
<Quantity quantity="results.method.method_name" label='method name' noWrap {...quantityProps}/>
{data?.results?.method?.method_name === 'DFT' && <>
<Quantity quantity="results.method.simulation.dft.xc_functional_type" label='xc functional family' noWrap {...quantityProps}/>
<Quantity quantity="results.method.simulation.dft.xc_functional_names" label='xc functional names' noWrap {...quantityProps}/>
......@@ -450,7 +447,7 @@ export default function DFTEntryOverview({data}) {
</Typography>
</Quantity>
<Quantity
desciption={searchQuantities['datasets'] && searchQuantities['datasets'].description}
description={searchQuantities['datasets'] && searchQuantities['datasets'].description}
label='datasets'
placeholder='no datasets'
{...quantityProps}
......@@ -470,7 +467,7 @@ export default function DFTEntryOverview({data}) {
<SidebarCard>
<Quantity column style={{maxWidth: 350}}>
<Quantity quantity="mainfile" noWrap ellipsisFront withClipboard {...quantityProps}/>
<Quantity quantity="entry_id" label='entry_id' noWrap withClipboard {...quantityProps}/>
<Quantity quantity="entry_id" label='entry id' noWrap withClipboard {...quantityProps}/>
<Quantity quantity="results.material.material_id" label='material id' noWrap withClipboard {...quantityProps}/>
<Quantity quantity="upload_id" label='upload id' noWrap withClipboard {...quantityProps}/>
<Quantity quantity="upload_time" label='upload time' noWrap {...quantityProps}>
......@@ -528,7 +525,7 @@ export default function DFTEntryOverview({data}) {
/>
<Quantity
description="Space group symbol and number"
label="spacegroup"
label="space group"
hideIfUnavailable
noWrap
{...quantityProps}
......
......@@ -20,7 +20,7 @@ import React from 'react'
import 'regenerator-runtime/runtime'
import DFTEntryOverview from './DFTEntryOverview'
import { renderWithAPIRouter } from '../../testutils'
import { screen } from '@testing-library/react'
import { screen, waitForElementToBeRemoved } from '@testing-library/react'
import { within } from '@testing-library/dom'
import { repoDftBulk } from '../../../tests/repo'
import '@testing-library/jest-dom/extend-expect'
......@@ -36,40 +36,40 @@ describe('<DFTEntryOverview />', () => {
// The data from repository should show up immediately, as it has been
// loaded already.
const code_name = screen.getByTitle('The name of the used code.') // We need to wait a bit for the component to render
expect(within(code_name).getByText('code name')).toBeInTheDocument()
expect(within(code_name).getByText(repo.dft.code_name)).toBeInTheDocument()
const code_version = screen.getByTitle('The version of the used code.')
expect(within(code_version).getByText('code version')).toBeInTheDocument()
expect(within(code_version).getByText(repo.dft.code_version)).toBeInTheDocument()
const code_name = screen.getByTitle('The name of the used program.') // We need to wait a bit for the component to render
expect(within(code_name).getByText('program name')).toBeInTheDocument()
expect(within(code_name).getByText(repo.results.method.simulation.program_name)).toBeInTheDocument()
const code_version = screen.getByTitle('The version of the used program.')
expect(within(code_version).getByText('program version')).toBeInTheDocument()
expect(within(code_version).getByText(repo.results.method.simulation.program_version)).toBeInTheDocument()
const xc_family = screen.getByTitle('The libXC based xc functional classification used in the simulation.')
expect(within(xc_family).getByText('xc functional family')).toBeInTheDocument()
expect(within(xc_family).getByText(repo.dft.xc_functional)).toBeInTheDocument()
expect(within(xc_family).getByText(repo.results.method.simulation.dft.xc_functional_type)).toBeInTheDocument()
const xc_names = screen.getByTitle('The list of libXC functional names that where used in this entry.')
expect(within(xc_names).getByText('xc functional names')).toBeInTheDocument()
expect(within(xc_names).getByText(repo.dft.xc_functional_names.join(', '))).toBeInTheDocument()
expect(within(xc_names).getByText(repo.results.method.simulation.dft.xc_functional_names.join(', '))).toBeInTheDocument()
const basis_set_type = screen.getByTitle('The used basis set functions.')
expect(within(basis_set_type).getByText('basis set type')).toBeInTheDocument()
expect(within(basis_set_type).getByText(repo.dft.basis_set)).toBeInTheDocument()
expect(within(basis_set_type).getByText(repo.results.method.simulation.dft.basis_set_type)).toBeInTheDocument()
const comment = screen.getByTitle('A user provided comment for this entry')
expect(within(comment).getByText('comment')).toBeInTheDocument()
expect(within(comment).getByText(repo.comment)).toBeInTheDocument()
const references = screen.getByTitle('User provided references (URLs) for this entry')
expect(within(references).getByText('references')).toBeInTheDocument()
expect(within(references).getByText(repo.references[0])).toBeInTheDocument()
const authors = screen.getByTitle('The full name of the authors for exact searches')
const authors = screen.getByTitle('All authors (uploader and co-authors)')
expect(within(authors).getByText('authors')).toBeInTheDocument()
expect(within(authors).getByText(repo.authors[0].name)).toBeInTheDocument()
const datasets = screen.getByTitle('A list of user curated datasets this entry belongs to for exact name search')
const datasets = screen.getByTitle('A list of user curated datasets this entry belongs to.')
expect(within(datasets).getByText('datasets')).toBeInTheDocument()
expect(within(datasets).getByText(repo.datasets[0].name)).toBeInTheDocument()
const mainfile = screen.getByTitle('Search within the mainfile path.')
const mainfile = screen.getByTitle('The path to the mainfile from the root directory of the uploaded files')
expect(within(mainfile).getByText('mainfile')).toBeInTheDocument()
expect(within(mainfile).getByText(repo.mainfile)).toBeInTheDocument()
const entry_id = screen.getByTitle('A persistent and globally unique identifier for the entry')
const entry_id = screen.getByTitle('The unique primary id for this entry.')
expect(within(entry_id).getByText('entry id')).toBeInTheDocument()
expect(within(entry_id).getByText(repo.calc_id)).toBeInTheDocument()
const material_id = screen.getByTitle('Search for a particular material by its id.')
const material_id = screen.getByTitle('A fixed length, unique material identifier in the form of a hash digest.')
expect(within(material_id).getByText('material id')).toBeInTheDocument()
expect(within(material_id).getByText(repo.encyclopedia.material.material_id)).toBeInTheDocument()
const upload_id = screen.getByTitle('The persistent and globally unique identifier for the upload of the entry')
......@@ -84,33 +84,40 @@ describe('<DFTEntryOverview />', () => {
const processing_version = screen.getByTitle('Version used in the last processing')
expect(within(processing_version).getByText('processing version')).toBeInTheDocument()
expect(within(processing_version).getByText(`${repo.nomad_version}/${repo.nomad_commit}`)).toBeInTheDocument()
const formula = screen.getByTitle('A (reduced) chemical formula')
const formula = screen.getByTitle('The chemical formula for a structure in Hill form with element symbols followed by integer chemical proportion numbers. The proportion number MUST be omitted if it is 1.')
expect(within(formula).getByText('formula')).toBeInTheDocument()
expect(within(formula).getByText(repo.formula)).toBeInTheDocument()
const material_type = screen.getByTitle('The system type of the simulated system.')
expect(within(material_type).getByText('material type')).toBeInTheDocument()
expect(within(material_type).getByText(repo.dft.system)).toBeInTheDocument()
const material_name = screen.getByTitle('Most meaningful name for a material if one could be assigned')
expect(within(formula).getByText(repo.results.material.chemical_formula_hill)).toBeInTheDocument()
const structural_type = screen.getByTitle('Classification based on structural features.')
expect(within(structural_type).getByText('structural type')).toBeInTheDocument()
expect(within(structural_type).getByText(repo.results.material.type_structural)).toBeInTheDocument()
const material_name = screen.getByTitle('Meaningful names for this a material if any can be assigned.')
expect(within(material_name).getByText('material name')).toBeInTheDocument()
expect(within(material_name).getByText(repo.encyclopedia.material.material_name)).toBeInTheDocument()
const crystal_system = screen.getByTitle('The crystal system type of the simulated system.')
expect(within(material_name).getByText(repo.results.material.material_name)).toBeInTheDocument()
const crystal_system = screen.getByTitle('Name of the crystal system. Can be one of the following: triclinic, monoclinic, orthorhombic, tetragonal, trigonal, hexagonal or cubic.')
expect(within(crystal_system).getByText('crystal system')).toBeInTheDocument()
expect(within(crystal_system).getByText(repo.dft.crystal_system)).toBeInTheDocument()
const spacegroup = screen.getByTitle('The space group symbol and space group number')
expect(within(spacegroup).getByText('spacegroup')).toBeInTheDocument()
expect(within(spacegroup).getByText(`${repo.dft.spacegroup_symbol} (${repo.dft.spacegroup})`)).toBeInTheDocument()
// The data from archive should show placeholders until it is loaded
const es_method = screen.getByTitle('The used electronic structure method.')
expect(within(es_method).getByText('electronic structure method')).toBeInTheDocument()
expect(within(es_method).getByText('loading ...')).toBeInTheDocument()
expect(within(crystal_system).getByText(repo.results.material.symmetry.crystal_system)).toBeInTheDocument()
const space_group = screen.getByTitle('Space group symbol and number')
expect(within(space_group).getByText('space group')).toBeInTheDocument()
expect(within(space_group).getByText(`${repo.results.material.symmetry.space_group_symbol} (${repo.results.material.symmetry.space_group_number})`)).toBeInTheDocument()
const method_name = screen.getByTitle('Common name for the used method.')
expect(within(method_name).getByText('method name')).toBeInTheDocument()
expect(within(method_name).getByText('DFT')).toBeInTheDocument()
// The test DOM does not support canvas or WebGL, and trying to add mocks
// for them does not seem to work ATM. Thus we expect a message saying that
// the 3D viewers are disabled.
expect(screen.getByText('Could not display the visualization as your browser does not support WebGL content.')).toBeInTheDocument()
// When the archived data is loaded, check that it is displayed correctly
expect(await within(es_method).findByText('DFT')).toBeInTheDocument()
// The data from archive should show placeholders until it is loaded
const dos_electronic = screen.getByTestId('dos-electronic-overview')
const dos_electronic_placeholder = screen.getByTestId('dos-electronic-overview-placeholder')
expect(dos_electronic).toBeInTheDocument()
expect(dos_electronic_placeholder).toBeInTheDocument()
// When the archived data is loaded, check that the placeholders are removed
// and the data is displayed correctly.
waitForElementToBeRemoved(dos_electronic_placeholder).then(() => {
expect(dos_electronic).not.toBeInTheDocument()
})
})
})
......@@ -53,7 +53,7 @@ export default function RawFileView({uploadId, entryId}) {
}, [api, raiseError, entryId, setState])
const entryData = state.entryData || {uploadId: uploadId, entryId: entryId}
const domainComponent = calcData.domain && domainComponents[calcData.domain]
const domainComponent = entryData.domain && domainComponents[entryData.domain]
if (state.doesNotExist) {
return <EntryPageContent>
......@@ -65,8 +65,7 @@ export default function RawFileView({uploadId, entryId}) {
return (
<EntryPageContent maxWidth={'1024px'} width={'100%'} minWidth={'800px'}>
{domain && <domain.EntryRawView data={entryData} entryId={entryId} uploadId={uploadId} />}
{domainComponent && <domainComponent.EntryRawView data={calcData} entryId={entryId} uploadId={uploadId} />}
{domainComponent && <domainComponent.EntryRawView data={entryData} entryId={entryId} uploadId={uploadId} />}
</EntryPageContent>
)
}
......
......@@ -19,7 +19,7 @@ import React from 'react'
import PropTypes from 'prop-types'
import { SnackbarContent, IconButton, Snackbar, withStyles, Typography, Box } from '@material-ui/core'
import CloseIcon from '@material-ui/icons/Close'
import { serviceWorkerRegistrationRef } from '..'
import { serviceWorkerRegistrationRef } from '../serviceWorker'
import Markdown from './Markdown'
export class VersionMismatch extends Error {
......
......@@ -26,7 +26,7 @@ import AppBar from './AppBar'
import { version } from '../../config'
import Routes from './Routes'
import { withApi } from '../api'
import { serviceWorkerUpdateHandlerRef } from '../../index'
import { serviceWorkerUpdateHandlerRef } from '../../serviceWorker'
import { ErrorBoundary } from '../errors'
export const ScrollContext = React.createContext({scrollParentRef: null})
......
......@@ -32,7 +32,18 @@ const useStyles = makeStyles({
root: {}
})
function DOS({data, layout, aspectRatio, className, classes, placeholderStyle, unitsState, type, ...other}) {
function DOS({
data,
layout,
aspectRatio,
className,
classes,
placeholderStyle,
unitsState,
type,
'data-testid': testID,
...other
}) {
// Merge custom layout with default layout
const units = useRecoilValue(unitsState)
const initialLayout = useMemo(() => {
......@@ -179,6 +190,7 @@ function DOS({data, layout, aspectRatio, className, classes, placeholderStyle, u
floatTitle="Density of states"
warning={normalizedToHOE === false ? normalizationWarning : null}
{...other}
data-testid={testID}
>
</Plot>
</Box>
......@@ -194,7 +206,8 @@ DOS.propTypes = {
placeholderStyle: PropTypes.string,
noDataStyle: PropTypes.string,
unitsState: PropTypes.object, // Recoil atom containing the unit configuration
type: PropTypes.string // Type of band structure: electronic or vibrational
type: PropTypes.string, // Type of band structure: electronic or vibrational
'data-testid': PropTypes.string
}
DOS.defaultProps = {
type: 'electronic'
......
......@@ -121,6 +121,7 @@ function ElectronicProperties({bs, dos, className, classes}) {
unitsState={unitsState}
layoutSubject={bsYSubject}
metaInfoLink={dos?.path}
data-testid="dos-electronic-overview"
></DOS>
</Box>
{bs !== false
......
......@@ -79,17 +79,7 @@ export default function Placeholder(props) {
Placeholder.propTypes = {
aspectRatio: PropTypes.number,
paddingTop: PropTypes.string,
paddingBottom: PropTypes.string,
paddingLeft: PropTypes.string,
paddingRight: PropTypes.string,
className: PropTypes.string,
classes: PropTypes.object
classes: PropTypes.object,
'data-testid': PropTypes.string
}
Placeholder.defaultProps = {
paddingTop: '0.5rem',
paddingBottom: '0.5rem',
paddingLeft: '0.5rem',
paddingRight: '0.5rem'
}
......@@ -60,7 +60,8 @@ export default function Plot({
onRelayouting,
onHover,
onReset,
layoutSubject
layoutSubject,
'data-testid': testID
}) {
// States
const [float, setFloat] = useState(false)
......@@ -368,19 +369,25 @@ export default function Plot({
// If data is set explicitly to False, we show the NoData component.
if (data === false) {
return <Box className={clsx(className, styles.root)} position='relative' width='100%'>
<NoData aspectRatio={aspectRatio} classes={{placeholder: noDataStyle}}/>
return <Box className={clsx(className, styles.root)} position='relative' width='100%' data-testid={testID}>
<NoData aspectRatio={aspectRatio} classes={{placeholder: noDataStyle}} data-testid={`${testID}-nodata`}/>
</Box>
}
// Even if the plots are still loading, all the html elements need to be
// placed in the DOM. During loading, they are placed underneath the
// placeholder with visibility=hidden. This way Plotly still has access to
// these HTML nodes and their sizes when the plots are loading.
return <Box className={clsx(className, styles.root)} position='relative' width='100%'>
{loading && <Placeholder className={styles.placeHolder} classes={{placeholder: placeHolderStyle}} variant="rect" aspectRatio={aspectRatio}></Placeholder>}
return <Box className={clsx(className, styles.root)} position='relative' width='100%' data-testid={testID}>
{loading && <Placeholder
className={styles.placeHolder}
classes={{placeholder: placeHolderStyle}}
variant="rect"
aspectRatio={aspectRatio}
data-testid={`${testID}-placeholder`}
></Placeholder>}
<Floatable className={styles.floatable} float={float} onFloat={() => setFloat(!float)} aspectRatio={aspectRatio}>
{float && <Typography variant="h6">{floatTitle}</Typography>}
<div ref={canvasRef} style={{width: '100%', height: '100%', position: 'relative'}}>
<div ref={canvasRef} style={{width: '100%', height: '100%', position: 'relative'}} data-testid="testi">
{warning && <Tooltip title={warning}>
<Warning className={styles.warning}></Warning>
</Tooltip>}
......@@ -416,7 +423,8 @@ Plot.propTypes = {
* rendering of the component. Should send messages that contain the new
* layout object.
*/
layoutSubject: PropTypes.any
layoutSubject: PropTypes.any,
'data-testid': PropTypes.string
}
Plot.defaultProps = {
aspectRatio: 9 / 16,
......
......@@ -24,22 +24,18 @@ import App from './components/App'
import '@fontsource/titillium-web/400.css'
import '@fontsource/titillium-web/600.css'
import '@fontsource/material-icons'
import * as serviceWorker from './serviceWorker'
export const serviceWorkerUpdateHandlerRef = {
current: null
}
export const serviceWorkerRegistrationRef = {
current: null
}
import {
register,
serviceWorkerUpdateHandlerRef,
serviceWorkerRegistrationRef
} from './serviceWorker'
ReactDOM.render(<App />, document.getElementById('root'))
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.register({
register({
onSuccess: registration => {
serviceWorkerRegistrationRef.current = registration
setInterval(() => {
......
......@@ -37,6 +37,14 @@ const isLocalhost = Boolean(
)
)
export const serviceWorkerUpdateHandlerRef = {
current: null
}
export const serviceWorkerRegistrationRef = {
current: null
}
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
......
......@@ -19,10 +19,10 @@
import React from 'react'
import { compose } from 'recompose'
import { render } from '@testing-library/react'
import { apiContext } from './components/api'
import { apiContext as apiContextV0 } from './components/api'
import { apiContext as apiContextV1 } from './components/apiv1'
import { Router } from 'react-router-dom'
import { archiveDftBulk } from '../tests/archive'
import { errorContext } from './components/errors'
import history from './history'
/**
......@@ -65,7 +65,7 @@ export function useKeycloakMock() {
authenticated = false
},
profile: userProfile,
realm: 'DemoRealm',
realm: 'test_realm',
realmAccess,
refreshToken: token,
token
......@@ -85,43 +85,41 @@ export function withKeycloakMock(Component) {
}
/**
* HOC for injecting a mocked Error handler implementation.
* HOC for injecting a mocked API implementation.
*/
export function withErrorsMock(Component) {
function WithErrorComponent(props) {
return (
<errorContext.Provider value={{ errrors: [], raiseError: (e) => { throw e } }}>
<errorContext.Consumer>
{errorContext => <Component {...props} raiseError={errorContext.raiseError} />}
</errorContext.Consumer>
</errorContext.Provider>
)
export function withApiV0Mock(Component) {
const apiValue = {
api: {
archive: (upload_id, calc_id) => {
return wait(archiveDftBulk)
}
}
}
return WithErrorComponent
return <apiContextV0.Provider value={apiValue}>
{Component}
</apiContextV0.Provider>
}
// export const ApiProviderMock = compose(withKeycloakMock, withErrorsMock)(ApiProviderComponent)
/**
* HOC for injecting a mocked API implementation.
*/
export function withApiMock(Component) {
export function withApiV1Mock(Component) {
const apiValue = {
api: {
archive: (upload_id, calc_id) => {
return wait(archiveDftBulk, 200)
results: (entry_id) => {
return wait(archiveDftBulk)
}
}
}
return <apiContext.Provider value={apiValue}>
return <apiContextV1.Provider value={apiValue}>
{Component}
</apiContext.Provider>
</apiContextV1.Provider>
}
/**
* HOC for Router dependency injection
*/
export function withRouter(Component) {
export function withRouterMock(Component) {
return <Router history={history}>
{Component}
</Router>
......@@ -132,11 +130,18 @@ export function withRouter(Component) {
*/
export function renderWithAPIRouter(component) {
return render(compose(
withRouter,
withApiMock
withRouterMock,
withApiV0Mock,
withApiV1Mock
)(component))
}
function wait(value, ms) {
/**
* Utility function for emulating delayed execution.
*
* @param {*} value value to return after delay
* @param {number} ms delay in milliseconds
*/
function wait(value, ms = 200) {
return new Promise(resolve => setTimeout(() => resolve(value), ms))
}
......@@ -3,14 +3,14 @@
*
* This file is part of NOMAD. See https://nomad-lab.eu for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
......@@ -25,6 +25,7 @@ export const repoDftBulk = {
nomad_version: '0.10.0',
nomad_commit: 'bf3c06fa',
calc_id: 'dft_bulk',
entry_id: 'dft_bulk',
comment: 'Mocked',
references: ['doi'],
authors: [{name: 'Lauri Himanen'}],
......@@ -41,6 +42,7 @@ export const repoDftBulk = {
crystal_system: 'cubic',
spacegroup_symbol: 'Fd-3m',
spacegroup: 227,
searchable_quantities: ['electronic_dos']
},
encyclopedia: {
material: {
......@@ -48,5 +50,44 @@ export const repoDftBulk = {
material_type: 'bulk',
material_name: 'Silicon'
}
},
results: {
material: {
material_id: 'Mock material id',
material_name: 'Silicon',
type_structural: 'bulk',
chemical_formula_reduced: 'Si2',
chemical_formula_hill: 'Si2',
chemical_formula_anonymous: 'A2',
chemical_formula_descriptive: 'Si2',
symmetry: {
crystal_system: 'cubic',
space_group_symbol: 'Fd-3m',
space_group_number: 227,
}
},
method: {
method_name: 'DFT',
simulation: {
program_name: 'VASP',
program_version: '1',
dft: {
basis_set_type: 'plane waves',
xc_functional_type: 'GGA',
xc_functional_names: ['GGA_C_PBE', 'GGA_X_PBE']
}
}
},
properties: {
available_properties: ["electronic.dos_electronic"],
electronic: {
dos_electronic: {
energy_highest_occupied: [0],
energy_lowest_unoccupied: [1],
energies: [0, 1],
densities: [0, 1],
}
}
}
}
}
This diff is collapsed.
......@@ -103,11 +103,13 @@ def metainfo_undecorated():
@dev.command(help='Generates a JSON with all search quantities.')
def search_quantities():
import json
# Add V0 searchable quantities
from nomad.search import v0 as search
# Due to this import, the parsing module will register all code_names based on parser
# implementations.
from nomad.parsing.parsers import parser_dict # pylint: disable=unused-import
import json