Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/siem-breadcrumbs-respect-basepat…
Browse files Browse the repository at this point in the history
…h' into siem-breadcrumbs-respect-basepath
  • Loading branch information
kqualters-elastic committed Jan 5, 2021
2 parents bca803b + 53d7fc1 commit cc7e51b
Show file tree
Hide file tree
Showing 255 changed files with 4,627 additions and 5,658 deletions.
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.15.3
14.15.4
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.15.3
14.15.4
6 changes: 6 additions & 0 deletions docs/user/dashboard/url-drilldown.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ Example:

`{{split event.value ","}}`

|encodeURIComponent
a|Escapes string using built in `encodeURIComponent` function.

|encodeURIQuery
a|Escapes string using built in `encodeURIComponent` function, while keeping "@", ":", "$", ",", and ";" characters as is.

|===


Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"**/typescript": "4.1.2"
},
"engines": {
"node": "14.15.3",
"node": "14.15.4",
"yarn": "^1.21.1"
},
"dependencies": {
Expand Down
44 changes: 44 additions & 0 deletions packages/kbn-i18n/GUIDELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,50 @@ Splitting sentences into several keys often inadvertently presumes a grammar, a

### Unit tests

#### How to test `FormattedMessage` and `i18n.translate()` components.

To make `FormattedMessage` component work properly, wrapping it with `I18nProvider` is required. In development/production app, this is done in the ancestor components and developers don't have to worry about that.

But when unit-testing them, no other component provides that wrapping. That's why `shallowWithI18nProvider` and `mountWithI18nProvider` helpers are created.

For example, there is a component that has `FormattedMessage` inside, like `SaveModal` component:

```js
// ...
export const SaveModal = (props) => {
return (
<div>
{/* Other things. */}
<EuiButton>
<FormattedMessage
id="kbn.dashboard.topNav.saveModal.saveButtonText"
defaultMessage="Save"
/>
</EuiButton>
{/* More other things. */}
</div>
)
}
```

To test `SaveModal` component, it should be wrapped with `I18nProvider` by using `shallowWithI18nProvider`:

```js
// ...
it('should render normally', async () => {
const component = shallowWithI18nProvider(
<SaveModal dashboard={{}}/>
);

expect(component).toMatchSnapshot();
});
// ...
```

If a component uses only `i18n.translate()`, it doesn't need `I18nProvider`. In that case, you can test them with `shallow` and `mount` functions that `enzyme` providers out of the box.

#### How to test `injectI18n` HOC components.

Testing React component that uses the `injectI18n` higher-order component is more complicated because `injectI18n()` creates a wrapper component around the original component.

With shallow rendering only top level component is rendered, that is a wrapper itself, not the original component. Since we want to test the rendering of the original component, we need to access it via the wrapper's `WrappedComponent` property. Its value will be the component we passed into `injectI18n()`.
Expand Down
2 changes: 2 additions & 0 deletions src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ export class DocLinksService {
createRoleMapping: `${ELASTICSEARCH_DOCS}security-api-put-role-mapping.html`,
createApiKey: `${ELASTICSEARCH_DOCS}security-api-create-api-key.html`,
createPipeline: `${ELASTICSEARCH_DOCS}put-pipeline-api.html`,
createTransformRequest: `${ELASTICSEARCH_DOCS}put-transform.html#put-transform-request-body`,
openIndex: `${ELASTICSEARCH_DOCS}indices-open-close.html`,
updateTransform: `${ELASTICSEARCH_DOCS}update-transform.html`,
},
},
});
Expand Down
21 changes: 1 addition & 20 deletions src/core/server/http/cookie_session_storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

import { Request, Server } from '@hapi/hapi';
import hapiAuthCookie from '@hapi/cookie';
// @ts-expect-error no TS definitions
import Statehood from '@hapi/statehood';

import { KibanaRequest, ensureRawRequest } from './router';
import { SessionStorageFactory, SessionStorage } from './session_storage';
Expand Down Expand Up @@ -148,7 +146,7 @@ export async function createCookieSessionStorageFactory<T>(
path: basePath === undefined ? '/' : basePath,
clearInvalid: false,
isHttpOnly: true,
isSameSite: cookieOptions.sameSite === 'None' ? false : cookieOptions.sameSite ?? false,
isSameSite: cookieOptions.sameSite ?? false,
},
validateFunc: async (req: Request, session: T | T[]) => {
const result = cookieOptions.validate(session);
Expand All @@ -159,23 +157,6 @@ export async function createCookieSessionStorageFactory<T>(
},
});

// A hack to support SameSite: 'None'.
// Remove it after update Hapi to v19 that supports SameSite: 'None' out of the box.
if (cookieOptions.sameSite === 'None') {
log.debug('Patching Statehood.prepareValue');
const originalPrepareValue = Statehood.prepareValue;
Statehood.prepareValue = function kibanaStatehoodPrepareValueWrapper(
name: string,
value: unknown,
options: any
) {
if (name === cookieOptions.name) {
options.isSameSite = cookieOptions.sameSite;
}
return originalPrepareValue(name, value, options);
};
}

return {
asScoped(request: KibanaRequest) {
return new ScopedCookieSessionStorage<T>(log, server, ensureRawRequest(request));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,8 @@ describe('migration actions', () => {
});
});

describe('createIndex', () => {
// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/87160
describe.skip('createIndex', () => {
afterAll(async () => {
await client.indices.delete({ index: 'yellow_then_green_index' });
});
Expand Down
10 changes: 5 additions & 5 deletions src/dev/cli_dev_mode/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export interface Log {
good(label: string, ...args: any[]): void;
warn(label: string, ...args: any[]): void;
bad(label: string, ...args: any[]): void;
write(label: string, ...args: any[]): void;
write(...args: any[]): void;
}

export class CliLog implements Log {
Expand Down Expand Up @@ -58,9 +58,9 @@ export class CliLog implements Log {
console.log(Chalk.white.bgRed(` ${label.trim()} `), ...args);
}

write(label: string, ...args: any[]) {
write(...args: any[]) {
// eslint-disable-next-line no-console
console.log(` ${label.trim()} `, ...args);
console.log(...args);
}
}

Expand Down Expand Up @@ -88,10 +88,10 @@ export class TestLog implements Log {
});
}

write(label: string, ...args: any[]) {
write(...args: any[]) {
this.messages.push({
type: 'write',
args: [label, ...args],
args,
});
}
}
4 changes: 2 additions & 2 deletions src/dev/cli_dev_mode/optimizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ it('is ready when optimizer phase is success or issue and logs in familiar forma
const lines = await linesPromise;
expect(lines).toMatchInlineSnapshot(`
Array [
"[2mnp bld[22m log [timestamp] [[36msuccess[39m][[95m@kbn/optimizer[39m] 0 bundles compiled successfully after 0 sec",
"[2mnp bld[22m log [timestamp] [error][[95m@kbn/optimizer[39m] webpack compile errors",
" [2mnp bld[22m log [timestamp] [[36msuccess[39m][[95m@kbn/optimizer[39m] 0 bundles compiled successfully after 0 sec",
" [2mnp bld[22m log [timestamp] [error][[95m@kbn/optimizer[39m] webpack compile errors",
]
`);
});
Expand Down
2 changes: 1 addition & 1 deletion src/dev/cli_dev_mode/optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class Optimizer {

ToolingLogTextWriter.write(
options.writeLogTo ?? process.stdout,
`${dim} log [${time()}] [${level(msg.type)}][${name}] `,
` ${dim} log [${time()}] [${level(msg.type)}][${name}] `,
msg
);
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import React from 'react';
import { I18nProvider } from '@kbn/i18n/react';
import { parse, ParsedQuery } from 'query-string';
import { render, unmountComponentAtNode } from 'react-dom';
import { Switch, Route, RouteComponentProps, HashRouter } from 'react-router-dom';
import { Switch, Route, RouteComponentProps, HashRouter, Redirect } from 'react-router-dom';

import { DashboardListing } from './listing';
import { DashboardApp } from './dashboard_app';
Expand Down Expand Up @@ -202,6 +202,9 @@ export async function mountApp({
render={renderDashboard}
/>
<Route exact path={DashboardConstants.LANDING_PAGE_PATH} render={renderListingPage} />
<Route path="/">
<Redirect to={DashboardConstants.LANDING_PAGE_PATH} />
</Route>
</Switch>
</HashRouter>
</KibanaContextProvider>
Expand Down
29 changes: 29 additions & 0 deletions src/plugins/expressions/public/react_expression_renderer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,35 @@ describe('ExpressionRenderer', () => {
instance.unmount();
});

it('should not update twice immediately after rendering', () => {
jest.useFakeTimers();

const refreshSubject = new Subject();
const loaderUpdate = jest.fn();

(ExpressionLoader as jest.Mock).mockImplementation(() => {
return {
render$: new Subject(),
data$: new Subject(),
loading$: new Subject(),
update: loaderUpdate,
destroy: jest.fn(),
};
});

const instance = mount(
<ReactExpressionRenderer reload$={refreshSubject} expression="" debounce={1000} />
);

act(() => {
jest.runAllTimers();
});

expect(loaderUpdate).toHaveBeenCalledTimes(1);

instance.unmount();
});

it('waits for debounce period on other loader option change if specified', () => {
jest.useFakeTimers();

Expand Down
5 changes: 5 additions & 0 deletions src/plugins/expressions/public/react_expression_renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ export const ReactExpressionRenderer = ({
);
const [debouncedExpression, setDebouncedExpression] = useState(expression);
const [waitingForDebounceToComplete, setDebouncePending] = useState(false);
const firstRender = useRef(true);
useShallowCompareEffect(() => {
if (firstRender.current) {
firstRender.current = false;
return;
}
if (debounce === undefined) {
return;
}
Expand Down
7 changes: 4 additions & 3 deletions x-pack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ Jest integration tests can be used to test behavior with Elasticsearch and the K
yarn test:jest_integration
```

An example test exists at [test_utils/jest/integration_tests/example_integration.test.ts](test_utils/jest/integration_tests/example_integration.test.ts)

#### Running Reporting functional tests

See [here](test/reporting/README.md) for more information on running reporting tests.
See [here](./test/functional/apps/dashboard/reporting/README.md) for more information on running reporting tests.

#### Running Security Solution Cypress E2E/integration tests
See [here](./plugins/security_solution/cypress/README.md) for information on running this test suite.
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ async function invalidateApiKeys(
encryptedSavedObjectsClient: EncryptedSavedObjectsClient,
securityPluginStart?: SecurityPluginStart
) {
// TODO: This could probably send a single request to ES now that the invalidate API supports multiple ids in a single request
let totalInvalidated = 0;
await Promise.all(
apiKeysToInvalidate.saved_objects.map(async (apiKeyObj) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ interface Props {

export function AgentConfigurationList({ status, data, refetch }: Props) {
const { core } = useApmPluginContext();
const canSave = core.application.capabilities.apm.save;
const { basePath } = core.http;
const { search } = useLocation();
const theme = useTheme();
Expand Down Expand Up @@ -180,28 +181,36 @@ export function AgentConfigurationList({ status, data, refetch }: Props) {
<TimestampTooltip time={value} timeUnit="minutes" />
),
},
{
width: px(units.double),
name: '',
render: (config: Config) => (
<EuiButtonIcon
aria-label="Edit"
iconType="pencil"
href={editAgentConfigurationHref(config.service, search, basePath)}
/>
),
},
{
width: px(units.double),
name: '',
render: (config: Config) => (
<EuiButtonIcon
aria-label="Delete"
iconType="trash"
onClick={() => setConfigToBeDeleted(config)}
/>
),
},
...(canSave
? [
{
width: px(units.double),
name: '',
render: (config: Config) => (
<EuiButtonIcon
aria-label="Edit"
iconType="pencil"
href={editAgentConfigurationHref(
config.service,
search,
basePath
)}
/>
),
},
{
width: px(units.double),
name: '',
render: (config: Config) => (
<EuiButtonIcon
aria-label="Delete"
iconType="trash"
onClick={() => setConfigToBeDeleted(config)}
/>
),
},
]
: []),
];

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiToolTip } from '@elastic/eui';
import {
EuiButton,
EuiFlexGroup,
Expand Down Expand Up @@ -73,15 +74,35 @@ function CreateConfigurationButton() {
const { basePath } = core.http;
const { search } = useLocation();
const href = createAgentConfigurationHref(search, basePath);
const canSave = core.application.capabilities.apm.save;
return (
<EuiFlexItem>
<EuiFlexGroup alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButton color="primary" fill iconType="plusInCircle" href={href}>
{i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', {
defaultMessage: 'Create configuration',
})}
</EuiButton>
<EuiToolTip
content={
!canSave &&
i18n.translate(
'xpack.apm.agentConfig.configurationsPanelTitle.noPermissionTooltipLabel',
{
defaultMessage:
"Your user role doesn't have permissions to create agent configurations",
}
)
}
>
<EuiButton
color="primary"
fill
iconType="plusInCircle"
href={href}
isDisabled={!canSave}
>
{i18n.translate('xpack.apm.agentConfig.createConfigButtonLabel', {
defaultMessage: 'Create configuration',
})}
</EuiButton>
</EuiToolTip>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Loading

0 comments on commit cc7e51b

Please sign in to comment.