Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-12-05 09:18:51 +00:00
parent 0e31e4396d
commit 01625f2465
22 changed files with 278 additions and 56 deletions

View File

@ -82,7 +82,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL/OpenSUSE
- Ruby (MRI) 3.0.5
- Ruby (MRI) 3.1.4
- Git 2.33+
- Redis 6.0+
- PostgreSQL 12+

View File

@ -6,6 +6,7 @@ import {
getDateInPast,
getCurrentUtcDate,
nWeeksBefore,
nYearsBefore,
} from '~/lib/utils/datetime_utility';
import { s__, __, sprintf, n__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
@ -251,3 +252,43 @@ export const METRICS_POPOVER_CONTENT = {
),
},
};
export const USAGE_OVERVIEW_NO_DATA_ERROR = s__(
'ValueStreamAnalytics|Failed to load usage overview data',
);
export const USAGE_OVERVIEW_DEFAULT_DATE_RANGE = {
endDate: TODAY,
startDate: nYearsBefore(TODAY, 1),
};
export const USAGE_OVERVIEW_IDENTIFIER_GROUPS = 'groups';
export const USAGE_OVERVIEW_IDENTIFIER_PROJECTS = 'projects';
export const USAGE_OVERVIEW_IDENTIFIER_ISSUES = 'issues';
export const USAGE_OVERVIEW_IDENTIFIER_MERGE_REQUESTS = 'merge_requests';
export const USAGE_OVERVIEW_IDENTIFIER_PIPELINES = 'pipelines';
// Defines the constants used for querying the API as well as the order they appear
export const USAGE_OVERVIEW_METADATA = {
[USAGE_OVERVIEW_IDENTIFIER_GROUPS]: { options: { title: __('Groups'), titleIcon: 'group' } },
[USAGE_OVERVIEW_IDENTIFIER_PROJECTS]: {
options: { title: __('Projects'), titleIcon: 'project' },
},
[USAGE_OVERVIEW_IDENTIFIER_ISSUES]: {
options: { title: __('Issues'), titleIcon: 'issues' },
},
[USAGE_OVERVIEW_IDENTIFIER_MERGE_REQUESTS]: {
options: { title: __('Merge requests'), titleIcon: 'merge-request' },
},
[USAGE_OVERVIEW_IDENTIFIER_PIPELINES]: {
options: { title: __('Pipelines'), titleIcon: 'pipeline' },
},
};
export const USAGE_OVERVIEW_QUERY_INCLUDE_KEYS = {
[USAGE_OVERVIEW_IDENTIFIER_GROUPS]: 'includeGroups',
[USAGE_OVERVIEW_IDENTIFIER_PROJECTS]: 'includeProjects',
[USAGE_OVERVIEW_IDENTIFIER_ISSUES]: 'includeIssues',
[USAGE_OVERVIEW_IDENTIFIER_MERGE_REQUESTS]: 'includeMergeRequests',
[USAGE_OVERVIEW_IDENTIFIER_PIPELINES]: 'includePipelines',
};

View File

@ -412,6 +412,15 @@ export const nYearsAfter = (date, numberOfYears) => {
return clone;
};
/**
* Returns the date `n` years before the date provided.
*
* @param {Date} date the initial date
* @param {Number} numberOfYears number of years before
* @return {Date} A `Date` object `n` years before the provided `Date`
*/
export const nYearsBefore = (date, numberOfYears) => nYearsAfter(date, -numberOfYears);
/**
* Returns the date after the date provided
*

View File

@ -33,6 +33,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
inject: ['isGroup'],
props: {
fullPath: {
type: String,
@ -152,6 +153,7 @@ export default {
note: this.note,
name,
fullPath: this.fullPath,
isGroup: this.isGroup,
workItemIid: this.workItemIid,
}),
});

View File

@ -8,6 +8,7 @@ export default {
components: {
AwardsList,
},
inject: ['isGroup'],
props: {
fullPath: {
type: String,
@ -73,6 +74,7 @@ export default {
note: this.note,
name,
fullPath: this.fullPath,
isGroup: this.isGroup,
workItemIid: this.workItemIid,
}),
});

View File

@ -28,6 +28,7 @@ import workItemNoteCreatedSubscription from '~/work_items/graphql/notes/work_ite
import workItemNoteUpdatedSubscription from '~/work_items/graphql/notes/work_item_note_updated.subscription.graphql';
import workItemNoteDeletedSubscription from '~/work_items/graphql/notes/work_item_note_deleted.subscription.graphql';
import deleteNoteMutation from '../graphql/notes/delete_work_item_notes.mutation.graphql';
import groupWorkItemNotesByIidQuery from '../graphql/notes/group_work_item_notes_by_iid.query.graphql';
import workItemNotesByIidQuery from '../graphql/notes/work_item_notes_by_iid.query.graphql';
import WorkItemAddNote from './notes/work_item_add_note.vue';
@ -46,6 +47,7 @@ export default {
WorkItemNotesActivityHeader,
WorkItemHistoryOnlyFilterNote,
},
inject: ['isGroup'],
props: {
fullPath: {
type: String,
@ -169,7 +171,9 @@ export default {
},
apollo: {
workItemNotes: {
query: workItemNotesByIidQuery,
query() {
return this.isGroup ? groupWorkItemNotesByIidQuery : workItemNotesByIidQuery;
},
variables() {
return {
fullPath: this.fullPath,

View File

@ -0,0 +1,32 @@
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
#import "ee_else_ce/work_items/graphql/notes/work_item_note.fragment.graphql"
query groupWorkItemNotesByIid($fullPath: ID!, $iid: String, $after: String, $pageSize: Int) {
workspace: group(fullPath: $fullPath) {
id
workItems(iid: $iid) {
nodes {
id
iid
widgets {
... on WorkItemWidgetNotes {
type
discussions(first: $pageSize, after: $after, filter: ALL_NOTES) {
pageInfo {
...PageInfo
}
nodes {
id
notes {
nodes {
...WorkItemNote
}
}
}
}
}
}
}
}
}
}

View File

@ -5,6 +5,7 @@ import {
updateCacheAfterAddingAwardEmojiToNote,
updateCacheAfterRemovingAwardEmojiFromNote,
} from '~/work_items/graphql/cache_utils';
import groupWorkItemNotesByIidQuery from '../graphql/notes/group_work_item_notes_by_iid.query.graphql';
import workItemNotesByIidQuery from '../graphql/notes/work_item_notes_by_iid.query.graphql';
import addAwardEmojiMutation from '../graphql/notes/work_item_note_add_award_emoji.mutation.graphql';
import removeAwardEmojiMutation from '../graphql/notes/work_item_note_remove_award_emoji.mutation.graphql';
@ -32,7 +33,7 @@ export function getMutation({ note, name }) {
};
}
export function optimisticAwardUpdate({ note, name, fullPath, workItemIid }) {
export function optimisticAwardUpdate({ note, name, fullPath, isGroup, workItemIid }) {
const { mutation } = getMutation({ note, name });
const currentUserId = window.gon.current_user_id;
@ -40,7 +41,7 @@ export function optimisticAwardUpdate({ note, name, fullPath, workItemIid }) {
return (store) => {
store.updateQuery(
{
query: workItemNotesByIidQuery,
query: isGroup ? groupWorkItemNotesByIidQuery : workItemNotesByIidQuery,
variables: { fullPath, iid: workItemIid },
},
(sourceData) => {

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
class CleanupCiStagesPipelineIdBigint < Gitlab::Database::Migration[2.2]
include Gitlab::Database::MigrationHelpers::WraparoundAutovacuum
disable_ddl_transaction!
milestone "16.7"
TABLE = :ci_stages
REFERENCING_TABLE = :ci_pipelines
COLUMN = :pipeline_id
OLD_COLUMN = :pipeline_id_convert_to_bigint
INDEXES = {
'index_ci_stages_on_pipeline_id_convert_to_bigint_and_name' => [
[:pipeline_id_convert_to_bigint, :name], { unique: true }
],
'index_ci_stages_on_pipeline_id_convert_to_bigint' => [
[:pipeline_id_convert_to_bigint], {}
],
'index_ci_stages_on_pipeline_id_convert_to_bigint_and_id' => [
[:pipeline_id_convert_to_bigint, :id], { where: 'status = ANY (ARRAY[0, 1, 2, 8, 9, 10])' }
],
'index_ci_stages_on_pipeline_id_convert_to_bigint_and_position' => [
[:pipeline_id_convert_to_bigint, :position], {}
]
}
OLD_FK_NAME = :fk_c5ddde695f
def up
return unless can_execute_on?(:ci_pipelines, :ci_stages)
with_lock_retries(raise_on_exhaustion: true) do
lock_tables(REFERENCING_TABLE, TABLE)
cleanup_conversion_of_integer_to_bigint(TABLE, [COLUMN])
end
end
def down
return unless can_execute_on?(:ci_pipelines, :ci_stages)
restore_conversion_of_integer_to_bigint(TABLE, [COLUMN])
INDEXES.each do |index_name, (columns, options)|
add_concurrent_index(TABLE, columns, name: index_name, **options)
end
add_concurrent_foreign_key(
TABLE, REFERENCING_TABLE,
column: OLD_COLUMN, name: OLD_FK_NAME,
on_delete: :cascade, validate: true, reverse_lock_order: true
)
end
end

View File

@ -0,0 +1 @@
7f3abae7002d20e30f9e4a30d580e49c5d72a7728d13ee45a5392fb4396da13b

View File

@ -531,15 +531,6 @@ RETURN NULL;
END
$$;
CREATE FUNCTION trigger_07bc3c48f407() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW."pipeline_id_convert_to_bigint" := NEW."pipeline_id";
RETURN NEW;
END;
$$;
CREATE FUNCTION trigger_10ee1357e825() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -14721,7 +14712,6 @@ ALTER SEQUENCE ci_sources_projects_id_seq OWNED BY ci_sources_projects.id;
CREATE TABLE ci_stages (
project_id integer,
pipeline_id_convert_to_bigint integer,
created_at timestamp without time zone,
updated_at timestamp without time zone,
name character varying,
@ -32344,14 +32334,6 @@ CREATE UNIQUE INDEX index_ci_stages_on_pipeline_id_and_name ON ci_stages USING b
CREATE INDEX index_ci_stages_on_pipeline_id_and_position ON ci_stages USING btree (pipeline_id, "position");
CREATE INDEX index_ci_stages_on_pipeline_id_convert_to_bigint ON ci_stages USING btree (pipeline_id_convert_to_bigint);
CREATE INDEX index_ci_stages_on_pipeline_id_convert_to_bigint_and_id ON ci_stages USING btree (pipeline_id_convert_to_bigint, id) WHERE (status = ANY (ARRAY[0, 1, 2, 8, 9, 10]));
CREATE UNIQUE INDEX index_ci_stages_on_pipeline_id_convert_to_bigint_and_name ON ci_stages USING btree (pipeline_id_convert_to_bigint, name);
CREATE INDEX index_ci_stages_on_pipeline_id_convert_to_bigint_and_position ON ci_stages USING btree (pipeline_id_convert_to_bigint, "position");
CREATE INDEX index_ci_stages_on_project_id ON ci_stages USING btree (project_id);
CREATE INDEX index_ci_subscriptions_projects_author_id ON ci_subscriptions_projects USING btree (author_id);
@ -37036,8 +37018,6 @@ CREATE TRIGGER push_rules_loose_fk_trigger AFTER DELETE ON push_rules REFERENCIN
CREATE TRIGGER tags_loose_fk_trigger AFTER DELETE ON tags REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
CREATE TRIGGER trigger_07bc3c48f407 BEFORE INSERT OR UPDATE ON ci_stages FOR EACH ROW EXECUTE FUNCTION trigger_07bc3c48f407();
CREATE TRIGGER trigger_10ee1357e825 BEFORE INSERT OR UPDATE ON p_ci_builds FOR EACH ROW EXECUTE FUNCTION trigger_10ee1357e825();
CREATE TRIGGER trigger_b2d852e1e2cb BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_b2d852e1e2cb();
@ -37953,9 +37933,6 @@ ALTER TABLE ONLY timelogs
ALTER TABLE ONLY geo_event_log
ADD CONSTRAINT fk_c4b1c1f66e FOREIGN KEY (repository_deleted_event_id) REFERENCES geo_repository_deleted_events(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_stages
ADD CONSTRAINT fk_c5ddde695f FOREIGN KEY (pipeline_id_convert_to_bigint) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_c63cbf6c25 FOREIGN KEY (closed_by_id) REFERENCES users(id) ON DELETE SET NULL;

View File

@ -37,7 +37,7 @@ Audit events can be viewed at the group, project, instance, and sign-in level. E
To view a group's audit events:
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Secure > Audit events**.
1. Select **Secure > Audit events**.
1. Filter the audit events by the member of the project (user) who performed the action and date range.
Group audit events can also be accessed using the [Group Audit Events API](../api/audit_events.md#group-audit-events). Group audit event queries are limited to a maximum of 30 days.
@ -45,7 +45,7 @@ Group audit events can also be accessed using the [Group Audit Events API](../ap
### Project audit events
1. On the left sidebar, select **Search or go to** and find your project.
1. On the left sidebar, select **Secure > Audit events**.
1. Select **Secure > Audit events**.
1. Filter the audit events by the member of the project (user) who performed the action and date range.
Project audit events can also be accessed using the [Project Audit Events API](../api/audit_events.md#project-audit-events). Project audit event queries are limited to a maximum of 30 days.
@ -56,7 +56,7 @@ You can view audit events from user actions across an entire GitLab instance.
To view instance audit events:
1. On the left sidebar, at the bottom, select **Admin Area**.
1. On the left sidebar, select **Monitoring > Audit Events**.
1. Select **Monitoring > Audit Events**.
1. Filter by the following:
- Member of the project (user) who performed the action
- Group
@ -82,7 +82,7 @@ You can export the current view (including filters) of your instance audit event
CSV(comma-separated values) file. To export the instance audit events to CSV:
1. On the left sidebar, at the bottom, select **Admin Area**.
1. On the left sidebar, select **Monitoring > Audit Events**.
1. Select **Monitoring > Audit Events**.
1. Select the available search filters.
1. Select **Export as CSV**.

View File

@ -1010,8 +1010,11 @@ To open the Admin Area:
```markdown
1. On the left sidebar, at the bottom, select **Admin Area**.
1. Select **Settings > CI/CD**.
```
You do not need to repeat `On the left sidebar` in your second step.
To open the **Your work** menu item:
```markdown

View File

@ -380,6 +380,18 @@ Use **compute minutes** instead of these (or similar) terms:
For more information, see [epic 2150](https://gitlab.com/groups/gitlab-com/-/epics/2150).
## configuration
When you update a collection of settings, call it a **configuration**.
## configure
Use **configure** after a feature or product has been [set up](#setup-set-up).
For example:
1. Set up your installation.
1. Configure your installation.
## confirmation dialog
Use **confirmation dialog** to describe the dialog that asks you to confirm an action. For example:
@ -1335,6 +1347,10 @@ see the [Microsoft Style Guide](https://learn.microsoft.com/en-us/style-guide/a-
Use **Premium**, in uppercase, for the subscription tier. When you refer to **Premium**
in the context of other subscription tiers, follow [the subscription tier](#subscription-tier) guidance.
## preferences
Use **preferences** to describe user-specific, system-level settings like theme and layout.
## prerequisites
Use **prerequisites** when documenting the tasks that must be completed or the conditions that must be met before a user can complete a task. Do not use **requirements**.
@ -1556,6 +1572,17 @@ Use **setup** as a noun, and **set up** as a verb. For example:
- Your remote office setup is amazing.
- To set up your remote office correctly, consider the ergonomics of your work area.
Do not confuse **set up** with [**configure**](#configure).
**Set up** implies that it's the first time you've done something. For example:
1. Set up your installation.
1. Configure your installation.
## settings
A **setting** changes the default behavior of the product. A **setting** consists of a key/value pair,
typically represented by a label with one or more options.
## sign in, sign-in
To describe the action of signing in, use:

View File

@ -24,7 +24,6 @@ To leave feedback about Product Analytics bugs or functionality:
- Comment on [issue 391970](https://gitlab.com/gitlab-org/gitlab/-/issues/391970).
- Create an issue with the `group::product analytics` label.
- [Schedule a call](https://calendly.com/jheimbuck/30-minute-call) with the team.
## How product analytics works

View File

@ -53029,6 +53029,9 @@ msgstr ""
msgid "ValueStreamAnalytics|Edit Value Stream: %{name}"
msgstr ""
msgid "ValueStreamAnalytics|Failed to load usage overview data"
msgstr ""
msgid "ValueStreamAnalytics|Go to docs"
msgstr ""

View File

@ -800,6 +800,21 @@ describe('date addition/subtraction methods', () => {
);
});
describe('nYearsBefore', () => {
it.each`
date | numberOfYears | expected
${'2020-07-06'} | ${4} | ${'2016-07-06'}
${'2020-07-06'} | ${1} | ${'2019-07-06'}
`(
'returns $expected for "$numberOfYears year(s) before $date"',
({ date, numberOfYears, expected }) => {
expect(datetimeUtility.nYearsBefore(new Date(date), numberOfYears)).toEqual(
new Date(expected),
);
},
);
});
describe('nMonthsBefore', () => {
// The previous month (February) has 28 days
const march2019 = '2019-03-15T00:00:00.000Z';

View File

@ -64,6 +64,7 @@ describe('Work Item Note Actions', () => {
projectName,
},
provide: {
isGroup: false,
glFeatures: {
workItemsMvc2: true,
},

View File

@ -9,6 +9,7 @@ import AwardsList from '~/vue_shared/components/awards_list.vue';
import WorkItemNoteAwardsList from '~/work_items/components/notes/work_item_note_awards_list.vue';
import addAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_add_award_emoji.mutation.graphql';
import removeAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_remove_award_emoji.mutation.graphql';
import groupWorkItemNotesByIidQuery from '~/work_items/graphql/notes/group_work_item_notes_by_iid.query.graphql';
import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql';
import {
mockWorkItemNotesResponseWithComments,
@ -45,6 +46,7 @@ describe('Work Item Note Awards List', () => {
const findAwardsList = () => wrapper.findComponent(AwardsList);
const createComponent = ({
isGroup = false,
note = firstNote,
addAwardEmojiMutationHandler = addAwardEmojiMutationSuccessHandler,
removeAwardEmojiMutationHandler = removeAwardEmojiMutationSuccessHandler,
@ -55,12 +57,15 @@ describe('Work Item Note Awards List', () => {
]);
apolloProvider.clients.defaultClient.writeQuery({
query: workItemNotesByIidQuery,
query: isGroup ? groupWorkItemNotesByIidQuery : workItemNotesByIidQuery,
variables: { fullPath, iid: workItemIid },
...mockWorkItemNotesResponseWithComments,
});
wrapper = shallowMount(WorkItemNoteAwardsList, {
provide: {
isGroup,
},
propsData: {
fullPath,
workItemIid,
@ -89,17 +94,20 @@ describe('Work Item Note Awards List', () => {
expect(findAwardsList().props('canAwardEmoji')).toBe(hasAwardEmojiPermission);
});
it('adds award if not already awarded', async () => {
createComponent();
await waitForPromises();
it.each([true, false])(
'adds award if not already awarded in both group and project contexts',
async (isGroup) => {
createComponent({ isGroup });
await waitForPromises();
findAwardsList().vm.$emit('award', EMOJI_THUMBSUP);
findAwardsList().vm.$emit('award', EMOJI_THUMBSUP);
expect(addAwardEmojiMutationSuccessHandler).toHaveBeenCalledWith({
awardableId: firstNote.id,
name: EMOJI_THUMBSUP,
});
});
expect(addAwardEmojiMutationSuccessHandler).toHaveBeenCalledWith({
awardableId: firstNote.id,
name: EMOJI_THUMBSUP,
});
},
);
it('emits error if awarding emoji fails', async () => {
createComponent({
@ -114,20 +122,23 @@ describe('Work Item Note Awards List', () => {
expect(wrapper.emitted('error')).toEqual([[__('Failed to add emoji. Please try again')]]);
});
it('removes award if already awarded', async () => {
const removeAwardEmojiMutationHandler = removeAwardEmojiMutationSuccessHandler;
it.each([true, false])(
'removes award if already awarded in both group and project contexts',
async (isGroup) => {
const removeAwardEmojiMutationHandler = removeAwardEmojiMutationSuccessHandler;
createComponent({ removeAwardEmojiMutationHandler });
createComponent({ isGroup, removeAwardEmojiMutationHandler });
findAwardsList().vm.$emit('award', EMOJI_THUMBSDOWN);
findAwardsList().vm.$emit('award', EMOJI_THUMBSDOWN);
await waitForPromises();
await waitForPromises();
expect(removeAwardEmojiMutationHandler).toHaveBeenCalledWith({
awardableId: firstNote.id,
name: EMOJI_THUMBSDOWN,
});
});
expect(removeAwardEmojiMutationHandler).toHaveBeenCalledWith({
awardableId: firstNote.id,
name: EMOJI_THUMBSDOWN,
});
},
);
it('restores award if remove fails', async () => {
createComponent({

View File

@ -10,6 +10,7 @@ import WorkItemNotes from '~/work_items/components/work_item_notes.vue';
import WorkItemDiscussion from '~/work_items/components/notes/work_item_discussion.vue';
import WorkItemAddNote from '~/work_items/components/notes/work_item_add_note.vue';
import WorkItemNotesActivityHeader from '~/work_items/components/notes/work_item_notes_activity_header.vue';
import groupWorkItemNotesByIidQuery from '~/work_items/graphql/notes/group_work_item_notes_by_iid.query.graphql';
import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql';
import deleteWorkItemNoteMutation from '~/work_items/graphql/notes/delete_work_item_notes.mutation.graphql';
import workItemNoteCreatedSubscription from '~/work_items/graphql/notes/work_item_note_created.subscription.graphql';
@ -63,6 +64,9 @@ describe('WorkItemNotes component', () => {
const findWorkItemCommentNoteAtIndex = (index) => findAllWorkItemCommentNotes().at(index);
const findDeleteNoteModal = () => wrapper.findComponent(GlModal);
const groupWorkItemNotesQueryHandler = jest
.fn()
.mockResolvedValue(mockWorkItemNotesByIidResponse);
const workItemNotesQueryHandler = jest.fn().mockResolvedValue(mockWorkItemNotesByIidResponse);
const workItemMoreNotesQueryHandler = jest.fn().mockResolvedValue(mockMoreWorkItemNotesResponse);
const workItemNotesWithCommentsQueryHandler = jest
@ -87,17 +91,22 @@ describe('WorkItemNotes component', () => {
workItemIid = mockWorkItemIid,
defaultWorkItemNotesQueryHandler = workItemNotesQueryHandler,
deleteWINoteMutationHandler = deleteWorkItemNoteMutationSuccessHandler,
isGroup = false,
isModal = false,
isWorkItemConfidential = false,
} = {}) => {
wrapper = shallowMount(WorkItemNotes, {
apolloProvider: createMockApollo([
[workItemNotesByIidQuery, defaultWorkItemNotesQueryHandler],
[groupWorkItemNotesByIidQuery, groupWorkItemNotesQueryHandler],
[deleteWorkItemNoteMutation, deleteWINoteMutationHandler],
[workItemNoteCreatedSubscription, notesCreateSubscriptionHandler],
[workItemNoteUpdatedSubscription, notesUpdateSubscriptionHandler],
[workItemNoteDeletedSubscription, notesDeleteSubscriptionHandler],
]),
provide: {
isGroup,
},
propsData: {
fullPath: 'test-path',
workItemId,
@ -354,4 +363,22 @@ describe('WorkItemNotes component', () => {
expect(findWorkItemCommentNoteAtIndex(0).props('isWorkItemConfidential')).toBe(true);
});
describe('when project context', () => {
it('calls the project work item query', async () => {
createComponent();
await waitForPromises();
expect(workItemNotesQueryHandler).toHaveBeenCalled();
});
});
describe('when group context', () => {
it('calls the group work item query', async () => {
createComponent({ isGroup: true });
await waitForPromises();
expect(groupWorkItemNotesQueryHandler).toHaveBeenCalled();
});
});
});

View File

@ -2,6 +2,7 @@ import { getMutation, optimisticAwardUpdate } from '~/work_items/notes/award_uti
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import mockApollo from 'helpers/mock_apollo_helper';
import { __ } from '~/locale';
import groupWorkItemNotesByIidQuery from '~/work_items/graphql/notes/group_work_item_notes_by_iid.query.graphql';
import workItemNotesByIidQuery from '~/work_items/graphql/notes/work_item_notes_by_iid.query.graphql';
import addAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_add_award_emoji.mutation.graphql';
import removeAwardEmojiMutation from '~/work_items/graphql/notes/work_item_note_remove_award_emoji.mutation.graphql';
@ -105,5 +106,22 @@ describe('Work item note award utils', () => {
expect(updatedNote.awardEmoji.nodes).toEqual([]);
});
it.each`
description | isGroup | query
${'calls project query when in project context'} | ${false} | ${workItemNotesByIidQuery}
${'calls group query when in group context'} | ${true} | ${groupWorkItemNotesByIidQuery}
`('$description', ({ isGroup, query }) => {
const note = firstNote;
const { name } = mockAwardEmojiThumbsUp;
const cacheSpy = { updateQuery: jest.fn() };
optimisticAwardUpdate({ note, name, fullPath, isGroup, workItemIid })(cacheSpy);
expect(cacheSpy.updateQuery).toHaveBeenCalledWith(
{ query, variables: { fullPath, iid: workItemIid } },
expect.any(Function),
);
});
});
});

View File

@ -42,10 +42,6 @@ ci_stages:
- index_ci_stages_on_pipeline_id
index_ci_stages_on_pipeline_id_and_position:
- index_ci_stages_on_pipeline_id
index_ci_stages_on_pipeline_id_convert_to_bigint_and_name:
- index_ci_stages_on_pipeline_id_convert_to_bigint
index_ci_stages_on_pipeline_id_convert_to_bigint_and_position:
- index_ci_stages_on_pipeline_id_convert_to_bigint
dast_site_tokens:
index_dast_site_token_on_project_id_and_url:
- index_dast_site_tokens_on_project_id