Skip to content
Snippets Groups Projects
Commit ec5c53b2 authored by Igor Drozdov's avatar Igor Drozdov :two:
Browse files

Merge branch 'id-use-paginated-tree-for-last-commit' into 'master'

Use paginated tree for the last commit query

See merge request !91890
parents 12a280b7 a3ed631e
No related branches found
No related tags found
No related merge requests found
Pipeline #588678911 passed
Pipeline: GitLab

#588698331

    ......@@ -49,10 +49,11 @@ export default {
    };
    },
    update: (data) => {
    const pipelines = data.project?.repository?.tree?.lastCommit?.pipelines?.edges;
    const lastCommit = data.project?.repository?.paginatedTree?.nodes[0]?.lastCommit;
    const pipelines = lastCommit?.pipelines?.edges;
    return {
    ...data.project?.repository?.tree?.lastCommit,
    ...lastCommit,
    pipeline: pipelines?.length && pipelines[0].node,
    };
    },
    ......
    ......@@ -4,43 +4,46 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
    id
    repository {
    __typename
    tree(path: $path, ref: $ref) {
    paginatedTree(path: $path, ref: $ref) {
    __typename
    lastCommit {
    nodes {
    __typename
    id
    sha
    title
    titleHtml
    descriptionHtml
    message
    webPath
    authoredDate
    authorName
    authorGravatar
    author {
    lastCommit {
    __typename
    id
    name
    avatarUrl
    sha
    title
    titleHtml
    descriptionHtml
    message
    webPath
    }
    signatureHtml
    pipelines(ref: $ref, first: 1) {
    __typename
    edges {
    authoredDate
    authorName
    authorGravatar
    author {
    __typename
    id
    name
    avatarUrl
    webPath
    }
    signatureHtml
    pipelines(ref: $ref, first: 1) {
    __typename
    node {
    edges {
    __typename
    id
    detailedStatus {
    node {
    __typename
    id
    detailsPath
    icon
    tooltip
    text
    group
    detailedStatus {
    __typename
    id
    detailsPath
    icon
    tooltip
    text
    group
    }
    }
    }
    }
    ......
    import Vue, { nextTick } from 'vue';
    import VueApollo from 'vue-apollo';
    import { GlLoadingIcon } from '@gitlab/ui';
    import { shallowMount } from '@vue/test-utils';
    import { nextTick } from 'vue';
    import createMockApollo from 'helpers/mock_apollo_helper';
    import waitForPromises from 'helpers/wait_for_promises';
    import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
    import LastCommit from '~/repository/components/last_commit.vue';
    import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
    let vm;
    function createCommitData(data = {}) {
    const defaultData = {
    sha: '123456789',
    title: 'Commit title',
    titleHtml: 'Commit title',
    message: 'Commit message',
    webPath: '/commit/123',
    authoredDate: '2019-01-01',
    author: {
    name: 'Test',
    avatarUrl: 'https://test.com',
    webPath: '/test',
    },
    pipeline: {
    import pathLastCommitQuery from 'shared_queries/repository/path_last_commit.query.graphql';
    import { refMock } from '../mock_data';
    let wrapper;
    let mockResolver;
    const findPipeline = () => wrapper.find('.js-commit-pipeline');
    const findTextExpander = () => wrapper.find('.text-expander');
    const findUserLink = () => wrapper.find('.js-user-link');
    const findUserAvatarLink = () => wrapper.findComponent(UserAvatarLink);
    const findLastCommitLabel = () => wrapper.findByTestId('last-commit-id-label');
    const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
    const findCommitRowDescription = () => wrapper.find('.commit-row-description');
    const findStatusBox = () => wrapper.find('.gpg-status-box');
    const findItemTitle = () => wrapper.find('.item-title');
    const defaultPipelineEdges = [
    {
    __typename: 'PipelineEdge',
    node: {
    __typename: 'Pipeline',
    id: 'gid://gitlab/Ci::Pipeline/167',
    detailedStatus: {
    __typename: 'DetailedStatus',
    id: 'id',
    detailsPath: 'https://test.com/pipeline',
    icon: 'failed',
    icon: 'status_running',
    tooltip: 'failed',
    text: 'failed',
    group: {},
    group: 'failed',
    },
    },
    };
    return Object.assign(defaultData, data);
    }
    function factory(commit = createCommitData(), loading = false) {
    vm = shallowMount(LastCommit, {
    mocks: {
    $apollo: {
    queries: {
    commit: {
    loading: true,
    },
    ];
    const defaultAuthor = {
    __typename: 'UserCore',
    id: 'gid://gitlab/User/1',
    name: 'Test',
    avatarUrl: 'https://test.com',
    webPath: '/test',
    };
    const defaultMessage = 'Commit title';
    const createCommitData = ({
    pipelineEdges = defaultPipelineEdges,
    author = defaultAuthor,
    descriptionHtml = '',
    signatureHtml = null,
    message = defaultMessage,
    }) => {
    return {
    data: {
    project: {
    __typename: 'Project',
    id: 'gid://gitlab/Project/6',
    repository: {
    __typename: 'Repository',
    paginatedTree: {
    __typename: 'TreeConnection',
    nodes: [
    {
    __typename: 'Tree',
    lastCommit: {
    __typename: 'Commit',
    id: 'gid://gitlab/CommitPresenter/123456789',
    sha: '123456789',
    title: 'Commit title',
    titleHtml: 'Commit title',
    descriptionHtml,
    message,
    webPath: '/commit/123',
    authoredDate: '2019-01-01',
    authorName: 'Test',
    authorGravatar: 'https://test.com',
    author,
    signatureHtml,
    pipelines: {
    __typename: 'PipelineConnection',
    edges: pipelineEdges,
    },
    },
    },
    ],
    },
    },
    },
    },
    });
    // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
    // eslint-disable-next-line no-restricted-syntax
    vm.setData({ commit });
    vm.vm.$apollo.queries.commit.loading = loading;
    }
    };
    };
    const emptyMessageClass = 'font-italic';
    const createComponent = async (data = {}) => {
    Vue.use(VueApollo);
    describe('Repository last commit component', () => {
    afterEach(() => {
    vm.destroy();
    const currentPath = 'path';
    mockResolver = jest.fn().mockResolvedValue(createCommitData(data));
    wrapper = shallowMountExtended(LastCommit, {
    apolloProvider: createMockApollo([[pathLastCommitQuery, mockResolver]]),
    propsData: { currentPath },
    mixins: [{ data: () => ({ ref: refMock }) }],
    });
    };
    afterEach(() => {
    wrapper.destroy();
    mockResolver = null;
    });
    describe('Repository last commit component', () => {
    it.each`
    loading | label
    ${true} | ${'shows'}
    ${false} | ${'hides'}
    `('$label when loading icon $loading is true', async ({ loading }) => {
    factory(createCommitData(), loading);
    `('$label when loading icon is $loading', async ({ loading }) => {
    createComponent();
    await nextTick();
    if (!loading) {
    await waitForPromises();
    }
    expect(vm.find(GlLoadingIcon).exists()).toBe(loading);
    expect(findLoadingIcon().exists()).toBe(loading);
    });
    it('renders commit widget', async () => {
    factory();
    createComponent();
    await waitForPromises();
    await nextTick();
    expect(vm.element).toMatchSnapshot();
    expect(wrapper.element).toMatchSnapshot();
    });
    it('renders short commit ID', async () => {
    factory();
    await nextTick();
    createComponent();
    await waitForPromises();
    expect(vm.find('[data-testid="last-commit-id-label"]').text()).toEqual('12345678');
    expect(findLastCommitLabel().text()).toBe('12345678');
    });
    it('hides pipeline components when pipeline does not exist', async () => {
    factory(createCommitData({ pipeline: null }));
    createComponent({ pipelineEdges: [] });
    await waitForPromises();
    await nextTick();
    expect(vm.find('.js-commit-pipeline').exists()).toBe(false);
    expect(findPipeline().exists()).toBe(false);
    });
    it('renders pipeline components', async () => {
    factory();
    await nextTick();
    it('renders pipeline components when pipeline exists', async () => {
    createComponent();
    await waitForPromises();
    expect(vm.find('.js-commit-pipeline').exists()).toBe(true);
    expect(findPipeline().exists()).toBe(true);
    });
    it('hides author component when author does not exist', async () => {
    factory(createCommitData({ author: null }));
    createComponent({ author: null });
    await waitForPromises();
    await nextTick();
    expect(vm.find('.js-user-link').exists()).toBe(false);
    expect(vm.find(UserAvatarLink).exists()).toBe(false);
    expect(findUserLink().exists()).toBe(false);
    expect(findUserAvatarLink().exists()).toBe(false);
    });
    it('does not render description expander when description is null', async () => {
    factory(createCommitData({ descriptionHtml: null }));
    await nextTick();
    createComponent();
    await waitForPromises();
    expect(vm.find('.text-expander').exists()).toBe(false);
    expect(vm.find('.commit-row-description').exists()).toBe(false);
    expect(findTextExpander().exists()).toBe(false);
    expect(findCommitRowDescription().exists()).toBe(false);
    });
    it('expands commit description when clicking expander', async () => {
    factory(createCommitData({ descriptionHtml: 'Test description' }));
    await nextTick();
    vm.find('.text-expander').vm.$emit('click');
    await nextTick();
    expect(vm.find('.commit-row-description').isVisible()).toBe(true);
    expect(vm.find('.text-expander').classes('open')).toBe(true);
    });
    it('strips the first newline of the description', async () => {
    factory(createCommitData({ descriptionHtml: '
Update ADOPTERS.md' }));
    await nextTick();
    expect(vm.find('.commit-row-description').html()).toBe(
    '<pre class="commit-row-description gl-mb-3">Update ADOPTERS.md</pre>',
    );
    describe('when the description is present', () => {
    beforeEach(async () => {
    createComponent({ descriptionHtml: '&#x000A;Update ADOPTERS.md' });
    await waitForPromises();
    });
    it('strips the first newline of the description', () => {
    expect(findCommitRowDescription().html()).toBe(
    '<pre class="commit-row-description gl-mb-3">Update ADOPTERS.md</pre>',
    );
    });
    it('expands commit description when clicking expander', async () => {
    findTextExpander().vm.$emit('click');
    await nextTick();
    expect(findCommitRowDescription().isVisible()).toBe(true);
    expect(findTextExpander().classes()).toContain('open');
    });
    });
    it('renders the signature HTML as returned by the backend', async () => {
    factory(
    createCommitData({
    signatureHtml: `<a
    class="btn gpg-status-box valid"
    data-content="signature-content"
    data-html="true"
    data-placement="top"
    data-title="signature-title"
    data-toggle="popover"
    role="button"
    tabindex="0"
    >
    Verified
    </a>`,
    }),
    );
    await nextTick();
    expect(vm.find('.gpg-status-box').html()).toBe(
    `<a class="btn gpg-status-box valid" data-content="signature-content" data-html="true" data-placement="top" data-title="signature-title" data-toggle="popover" role="button" tabindex="0">
    Verified
    </a>`,
    createComponent({
    signatureHtml: `<a
    class="btn gpg-status-box valid"
    data-content="signature-content"
    data-html="true"
    data-placement="top"
    data-title="signature-title"
    data-toggle="popover"
    role="button"
    tabindex="0"
    >Verified</a>`,
    });
    await waitForPromises();
    expect(findStatusBox().html()).toBe(
    `<a class="btn gpg-status-box valid" data-content="signature-content" data-html="true" data-placement="top" data-title="signature-title" data-toggle="popover" role="button" tabindex="0">Verified</a>`,
    );
    });
    it('sets correct CSS class if the commit message is empty', async () => {
    factory(createCommitData({ message: '' }));
    await nextTick();
    createComponent({ message: '' });
    await waitForPromises();
    expect(vm.find('.item-title').classes()).toContain(emptyMessageClass);
    expect(findItemTitle().classes()).toContain('font-italic');
    });
    });
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment