Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../../../../..',
roots: ['<rootDir>/x-pack/solutions/search/plugins/search_getting_started'],
coverageDirectory:
'<rootDir>/target/kibana-coverage/jest/x-pack/solutions/search/plugins/search_getting_started',
coverageReporters: ['text', 'html'],
collectCoverageFrom: [
'<rootDir>/x-pack/solutions/search/plugins/search_getting_started/{public,server}/**/*.{ts,tsx}',
],
};
15 changes: 14 additions & 1 deletion x-pack/solutions/search/plugins/search_getting_started/moon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,24 @@ tags:
- prod
- group-search
- private
- jest-unit-tests
fileGroups:
src:
- common/**/*
- public/**/*
- server/**/*
- test/scout/**/*
- '!target/**/*'
tasks: {}
tasks:
jest:
args:
- '--config'
- $projectRoot/jest.config.js
inputs:
- '@group(src)'
jestCI:
args:
- '--config'
- $projectRoot/jest.config.js
inputs:
- '@group(src)'
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { isNew } from './utils';

describe('isNew', () => {
const fakeNow = new Date('2025-02-18T12:00:00.000Z');

beforeAll(() => {
jest.useFakeTimers();
jest.setSystemTime(fakeNow);
});

afterAll(() => {
jest.useRealTimers();
});

it('returns true when publishedAt is less than 30 days ago', () => {
const twentyNineDaysAgo = new Date('2025-01-20T12:00:00.000Z');
expect(isNew(twentyNineDaysAgo)).toBe(true);
});

it('returns true when publishedAt is today', () => {
expect(isNew(fakeNow)).toBe(true);
});

it('returns false when publishedAt is more than 30 days ago', () => {
const thirtyOneDaysAgo = new Date('2025-01-18T12:00:00.000Z');
expect(isNew(thirtyOneDaysAgo)).toBe(false);
});

it('returns false when publishedAt is exactly 30 days ago', () => {
const thirtyDaysAgo = new Date('2025-01-19T12:00:00.000Z');
expect(isNew(thirtyDaysAgo)).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

/**
*
* @param publishedAt - The date the tutorial was published
* @returns true if the tutorial is new (less than 30 days old)
* This means the "New" badges will largely be timely for serverless customers only.
* https://docs.elastic.dev/kibana-dev-docs/serverless/release-overview#standard-release-cadence
* publishedAt should be set to around the date the tutorial is expected to land in production.
*/
export const isNew = (publishedAt: Date) => {
const thirtyDaysAgo = new Date(Date.now() - 1000 * 60 * 60 * 24 * 30);
return publishedAt > thirtyDaysAgo;
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,19 @@ import React, { useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { sortBy } from 'lodash';
import { orderBy } from 'lodash';
import { useKibana } from '../../hooks/use_kibana';
import { SearchGettingStartedSectionHeading } from '../section_heading';
import { useAssetBasePath } from '../../hooks/use_asset_base_path';
import { isNew } from '../../common/utils';
interface TutorialMetadata {
title: string;
dataTestSubj: string;
description: string;
request: string;
image: string;
buttonRef: React.RefObject<HTMLButtonElement>;
isNew?: boolean;
publishedAt: Date;
}
const EXPAND_LIMIT = 3;

Expand Down Expand Up @@ -63,6 +64,7 @@ export const ConsoleTutorialsGroup = () => {
request: consoleTutorials.basics,
image: `${assetBasePath}/search_window_illustration.svg`,
buttonRef: React.createRef<HTMLButtonElement>(),
publishedAt: new Date('2025-10-31'),
},
{
title: i18n.translate('xpack.searchGettingStarted.consoleTutorials.semanticTitle', {
Expand All @@ -79,6 +81,7 @@ export const ConsoleTutorialsGroup = () => {
request: consoleTutorials.semanticSearch,
image: `${assetBasePath}/search_results_illustration.svg`,
buttonRef: React.createRef<HTMLButtonElement>(),
publishedAt: new Date('2025-10-31'),
},
{
title: i18n.translate('xpack.searchGettingStarted.consoleTutorials.esqlTitle', {
Expand All @@ -92,6 +95,7 @@ export const ConsoleTutorialsGroup = () => {
request: consoleTutorials.esql,
image: `${assetBasePath}/search_observe_illustration.svg`,
buttonRef: React.createRef<HTMLButtonElement>(),
publishedAt: new Date('2025-10-31'),
},
{
title: i18n.translate('xpack.searchGettingStarted.consoleTutorials.agentBuilderTitle', {
Expand All @@ -101,13 +105,13 @@ export const ConsoleTutorialsGroup = () => {
description: i18n.translate(
'xpack.searchGettingStarted.consoleTutorials.agentBuilderDescription',
{
defaultMessage: 'Learn how to use the Agent Builder to create and manage agents.',
defaultMessage: 'Learn how to use the Agent Builder APIs to create and manage agents.',
}
),
request: consoleTutorials.agentBuilder,
image: `${assetBasePath}/search_task_automation.svg`,
buttonRef: React.createRef<HTMLButtonElement>(),
isNew: true,
publishedAt: new Date('2026-02-18'),
},
{
title: i18n.translate('xpack.searchGettingStarted.consoleTutorials.tsdsTitle', {
Expand All @@ -121,10 +125,13 @@ export const ConsoleTutorialsGroup = () => {
request: consoleTutorials.timeSeriesDataStreams,
image: `${assetBasePath}/search_hourglass.svg`,
buttonRef: React.createRef<HTMLButtonElement>(),
isNew: true,
publishedAt: new Date('2026-02-04'),
},
];
return sortBy(items, 'isNew').slice(0, expanded ? undefined : EXPAND_LIMIT);
return orderBy(items, ({ publishedAt }) => publishedAt.getTime(), ['desc']).slice(
0,
expanded ? undefined : EXPAND_LIMIT
);
}, [assetBasePath, expanded]);

return (
Expand All @@ -146,7 +153,7 @@ export const ConsoleTutorialsGroup = () => {
hasBorder
title={tutorial.title}
betaBadgeProps={{
label: tutorial.isNew
label: isNew(tutorial.publishedAt)
? i18n.translate('xpack.searchGettingStarted.consoleTutorials.badge', {
defaultMessage: 'New',
})
Expand Down