diff options
30 files changed, 371 insertions, 94 deletions
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index 91e5fb2a666..f2b79a88a4a 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -3,6 +3,8 @@ /* global ListLabel */ import queryData from '../utils/query_data'; +const PER_PAGE = 20; + class List { constructor (obj) { this.id = obj.id; @@ -58,7 +60,9 @@ class List { nextPage () { if (this.issuesSize > this.issues.length) { - this.page += 1; + if (this.issues.length / PER_PAGE >= 1) { + this.page += 1; + } return this.getIssues(false); } @@ -145,10 +149,7 @@ class List { } updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid) { - gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid) - .then(() => { - listFrom.getIssues(false); - }); + gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid); } findIssue (id) { diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 0aad95c2fe3..97f279e4be4 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -2,6 +2,8 @@ consistent-return, prefer-rest-params */ /* global Breakpoints */ +import { bytesToKiB } from './lib/utils/number_utils'; + const bind = function (fn, me) { return function () { return fn.apply(me, arguments); }; }; const AUTO_SCROLL_OFFSET = 75; const DOWN_BUILD_TRACE = '#down-build-trace'; @@ -20,6 +22,7 @@ window.Build = (function () { this.state = this.options.logState; this.buildStage = this.options.buildStage; this.$document = $(document); + this.logBytes = 0; this.updateDropdown = bind(this.updateDropdown, this); @@ -98,15 +101,22 @@ window.Build = (function () { if (log.append) { $buildContainer.append(log.html); + this.logBytes += log.size; } else { $buildContainer.html(log.html); - if (log.truncated) { - $('.js-truncated-info-size').html(` ${log.size} `); - this.$truncatedInfo.removeClass('hidden'); - this.initAffixTruncatedInfo(); - } else { - this.$truncatedInfo.addClass('hidden'); - } + this.logBytes = log.size; + } + + // if the incremental sum of logBytes we received is less than the total + // we need to show a message warning the user about that. + if (this.logBytes < log.total) { + // size is in bytes, we need to calculate KiB + const size = bytesToKiB(this.logBytes); + $('.js-truncated-info-size').html(`${size}`); + this.$truncatedInfo.removeClass('hidden'); + this.initAffixTruncatedInfo(); + } else { + this.$truncatedInfo.addClass('hidden'); } this.checkAutoscroll(); diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index f277e1dddc7..d88b7cd0e17 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -33,6 +33,7 @@ /* global Labels */ /* global Shortcuts */ /* global Sidebar */ +/* global ShortcutsWiki */ import Issue from './issue'; @@ -46,6 +47,7 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; +import ShortcutsWiki from './shortcuts_wiki'; const ShortcutsBlob = require('./shortcuts_blob'); @@ -416,7 +418,7 @@ const ShortcutsBlob = require('./shortcuts_blob'); break; case 'wikis': new gl.Wikis(); - shortcut_handler = new ShortcutsNavigation(); + shortcut_handler = new ShortcutsWiki(); new ZenMode(); new gl.GLForm($('.wiki-form')); break; diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js new file mode 100644 index 00000000000..1e96c7ab5cd --- /dev/null +++ b/app/assets/javascripts/lib/utils/constants.js @@ -0,0 +1,2 @@ +/* eslint-disable import/prefer-default-export */ +export const BYTES_IN_KIB = 1024; diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index e2bf69ee52e..f1b07408671 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -1,4 +1,4 @@ -/* eslint-disable import/prefer-default-export */ +import { BYTES_IN_KIB } from './constants'; /** * Function that allows a number with an X amount of decimals @@ -32,3 +32,13 @@ export function formatRelevantDigits(number) { } return formattedNumber; } + +/** + * Utility function that calculates KiB of the given bytes. + * + * @param {Number} number bytes + * @return {Number} KiB + */ +export function bytesToKiB(number) { + return number / BYTES_IN_KIB; +} diff --git a/app/assets/javascripts/shortcuts_wiki.js b/app/assets/javascripts/shortcuts_wiki.js new file mode 100644 index 00000000000..8a075062a48 --- /dev/null +++ b/app/assets/javascripts/shortcuts_wiki.js @@ -0,0 +1,16 @@ +/* eslint-disable class-methods-use-this */ +/* global Mousetrap */ +/* global ShortcutsNavigation */ + +import findAndFollowLink from './shortcuts_dashboard_navigation'; + +export default class ShortcutsWiki extends ShortcutsNavigation { + constructor() { + super(); + Mousetrap.bind('e', this.editWiki); + } + + editWiki() { + findAndFollowLink('.js-wiki-edit'); + } +} diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 144adbcdaef..411f1c4442b 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -61,8 +61,9 @@ .truncated-info { text-align: center; border-bottom: 1px solid; - background-color: $black-transparent; + background-color: $black; height: 45px; + padding: 15px; &.affix { top: 0; @@ -87,6 +88,16 @@ right: 5px; left: 5px; } + + .truncated-info-size { + margin: 0 5px; + } + + .raw-link { + color: inherit; + margin-left: 5px; + text-decoration: underline; + } } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8c6dd392865..fe084eb9397 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -289,8 +289,12 @@ table.u2f-registrations { margin: 0 auto; .bordered-box { - border: 1px solid $border-color; + border: 1px solid $blue-300; border-radius: $border-radius-default; + background-color: $blue-25; + position: relative; + display: flex; + justify-content: center; } .landing { @@ -298,28 +302,59 @@ table.u2f-registrations { margin-bottom: $gl-padding; .close { - margin-right: 20px; - } + position: absolute; + right: 20px; + opacity: 1; + + .dismiss-icon { + float: right; + cursor: pointer; + color: $blue-300; + } - .dismiss-icon { - float: right; - cursor: pointer; - color: $cycle-analytics-dismiss-icon-color; + &:hover { + background-color: transparent; + border: 0; + + .dismiss-icon { + color: $blue-400; + } + } } .svg-container { - text-align: center; + margin-right: 30px; + display: inline-block; svg { - width: 136px; - height: 136px; + height: 110px; + vertical-align: top; } } + + .user-callout-copy { + display: inline-block; + vertical-align: top; + } } @media(max-width: $screen-xs-max) { - .inner-content { - padding-left: 30px; + text-align: center; + + .bordered-box { + display: block; + } + + .landing { + .svg-container, + .user-callout-copy { + margin: 0; + display: block; + + svg { + height: 75px; + } + } } } } diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 700c5e61a14..ea8bbe92d86 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -318,3 +318,11 @@ %td.shortcut .key l %td Change Label + %tbody.hidden-shortcut.wiki{ style: 'display:none' } + %tr + %th + %th Wiki pages + %tr + %td.shortcut + .key e + %td Edit wiki page diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 0faad57a312..7cb2ec83cc7 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -71,11 +71,11 @@ = custom_icon('scroll_down_hover_active') #up-build-trace %pre.build-trace#build-trace - .js-truncated-info.truncated-info.hidden - %span< - Showing last - %span.js-truncated-info-size>< - KiB of log + .js-truncated-info.truncated-info.hidden< + Showing last + %span.js-truncated-info-size.truncated-info-size>< + KiB of log - + %a.js-raw-link.raw-link{ :href => raw_namespace_project_build_path(@project.namespace, @project, @build) }>< Complete Raw %code.bash.js-build-output .build-loader-animation.js-build-refresh diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 03069804c86..da79ca2ee75 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -46,7 +46,7 @@ -# This tab is always loaded via AJAX - if @pipelines.any? #pipelines.pipelines.tab-pane - = render 'projects/merge_requests/show/pipelines', endpoint: url_for(params.merge(format: :json)) + = render 'projects/merge_requests/show/pipelines', endpoint: url_for(params.merge(format: :json)), disable_initialization: true .mr-loading-status = spinner diff --git a/app/views/projects/merge_requests/show/_pipelines.html.haml b/app/views/projects/merge_requests/show/_pipelines.html.haml index de4aa255bbd..2f1dbe87619 100644 --- a/app/views/projects/merge_requests/show/_pipelines.html.haml +++ b/app/views/projects/merge_requests/show/_pipelines.html.haml @@ -1,3 +1,4 @@ - endpoint_path = local_assigns[:endpoint] || pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :json) +- disable_initialization = local_assigns.fetch(:disable_initialization, false) -= render 'projects/commit/pipelines_list', endpoint: endpoint_path += render 'projects/commit/pipelines_list', endpoint: endpoint_path, disable_initialization: disable_initialization diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 86178257af8..6a578dbf640 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -5,5 +5,5 @@ = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do Page history - if can?(current_user, :create_wiki, @project) && @page.latest? - = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn" do + = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn js-wiki-edit" do Edit diff --git a/app/views/shared/_user_callout.html.haml b/app/views/shared/_user_callout.html.haml index 8f1293adcb1..8308baa7829 100644 --- a/app/views/shared/_user_callout.html.haml +++ b/app/views/shared/_user_callout.html.haml @@ -3,12 +3,11 @@ %button.btn.btn-default.close.js-close-callout{ type: 'button', 'aria-label' => 'Dismiss customize experience box' } = icon('times', class: 'dismiss-icon', 'aria-hidden' => 'true') - .row - .col-sm-3.col-xs-12.svg-container - = custom_icon('icon_customization') - .col-sm-8.col-xs-12.inner-content - %h4 - Customize your experience - %p - Change syntax themes, default project pages, and more in preferences. - = link_to 'Check it out', profile_preferences_path, class: 'btn btn-default js-close-callout' + .svg-container + = custom_icon('icon_customization') + .user-callout-copy + %h4 + Customize your experience + %p + Change syntax themes, default project pages, and more in preferences. + = link_to 'Check it out', profile_preferences_path, class: 'btn btn-primary js-close-callout' diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index b447996a8ab..f1350169bbe 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -12,13 +12,14 @@ class: "check_all_issues left" .issues-other-filters.filtered-search-wrapper .filtered-search-box - = dropdown_tag(content_tag(:i, '', class: 'fa fa-history'), - options: { wrapper_class: "filtered-search-history-dropdown-wrapper", - toggle_class: "filtered-search-history-dropdown-toggle-button", - dropdown_class: "filtered-search-history-dropdown", - content_class: "filtered-search-history-dropdown-content", - title: "Recent searches" }) do - .js-filtered-search-history-dropdown + - if type != :boards_modal && type != :boards + = dropdown_tag(content_tag(:i, '', class: 'fa fa-history'), + options: { wrapper_class: "filtered-search-history-dropdown-wrapper", + toggle_class: "filtered-search-history-dropdown-toggle-button", + dropdown_class: "filtered-search-history-dropdown", + content_class: "filtered-search-history-dropdown-content", + title: "Recent searches" }) do + .js-filtered-search-history-dropdown .filtered-search-box-input-container .scroll-container %ul.tokens-container.list-unstyled diff --git a/changelogs/unreleased/29595-customize-experience-callout.yml b/changelogs/unreleased/29595-customize-experience-callout.yml new file mode 100644 index 00000000000..ec8393142c6 --- /dev/null +++ b/changelogs/unreleased/29595-customize-experience-callout.yml @@ -0,0 +1,4 @@ +--- +title: 29595 Update callout design +merge_request: +author: diff --git a/changelogs/unreleased/29816-create-keyboard-shortcut-for-editing-wiki-page.yml b/changelogs/unreleased/29816-create-keyboard-shortcut-for-editing-wiki-page.yml new file mode 100644 index 00000000000..a165c70a6d3 --- /dev/null +++ b/changelogs/unreleased/29816-create-keyboard-shortcut-for-editing-wiki-page.yml @@ -0,0 +1,4 @@ +--- +title: Add keyboard edit shotcut for wiki +merge_request: 10245 +author: George Andrinopoulos diff --git a/changelogs/unreleased/enforce-Ansi2html-output-encoding.yml b/changelogs/unreleased/enforce-Ansi2html-output-encoding.yml new file mode 100644 index 00000000000..b1200548518 --- /dev/null +++ b/changelogs/unreleased/enforce-Ansi2html-output-encoding.yml @@ -0,0 +1,4 @@ +--- +title: Fix trace cannot be written due to encoding +merge_request: 10758 +author: diff --git a/doc/README.md b/doc/README.md index b6790b4d008..7703c64e152 100644 --- a/doc/README.md +++ b/doc/README.md @@ -6,10 +6,6 @@ All technical content published by GitLab lives in the documentation, including: - [User docs](#user-documentation): general documentation dedicated to regular users of GitLab - [Admin docs](#administrator-documentation): general documentation dedicated to administrators of GitLab instances - [Contributor docs](#contributor-documentation): general documentation on how to develop and contribute to GitLab -- [Topics](topics/index.md): pages organized per topic, gathering all the - resources already published by GitLab related to a specific subject, including - general docs, [technical articles](development/writing_documentation.md#technical-articles), - blog posts and video tutorials. - [GitLab University](university/README.md): guides to learn Git and GitLab through courses and videos. diff --git a/doc/user/search/img/search_history.gif b/doc/user/search/img/search_history.gif Binary files differnew file mode 100644 index 00000000000..4cfa48ee0ab --- /dev/null +++ b/doc/user/search/img/search_history.gif diff --git a/doc/user/search/index.md b/doc/user/search/index.md index 9d1ca1adcb2..45f443819ec 100644 --- a/doc/user/search/index.md +++ b/doc/user/search/index.md @@ -40,6 +40,12 @@ The same process is valid for merge requests. Navigate to your project's **Merge and click **Search or filter results...**. Merge requests can be filtered by author, assignee, milestone, and label. +## Search History + +You can view recent searches by clicking on the little arrow-clock icon, which is to the left of the search input. Click the search entry to run that search again. This feature is available for issues and merge requests. Searches are stored locally in your browser. + +![search history](img/search_history.gif) + ### Shortcut You'll also find a shortcut on the search field on the top-right of the project's dashboard to diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md index f94357abec9..c5b7488be69 100644 --- a/doc/workflow/shortcuts.md +++ b/doc/workflow/shortcuts.md @@ -75,3 +75,9 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?' | <kbd>r</kbd> | Reply (quoting selected text) | | <kbd>e</kbd> | Edit issue/merge request | | <kbd>l</kbd> | Change label | + +## Wiki pages + +| Keyboard Shortcut | Description | +| ----------------- | ----------- | +| <kbd>e</kbd> | Edit wiki page| diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb index 1020452480a..b439b0ee29b 100644 --- a/lib/ci/ansi2html.rb +++ b/lib/ci/ansi2html.rb @@ -172,7 +172,7 @@ module Ci close_open_tags() OpenStruct.new( - html: @out, + html: @out.force_encoding(Encoding.default_external), state: state, append: append, truncated: truncated, diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb index b929bdd55bc..fa462cbe095 100644 --- a/lib/gitlab/ci/trace/stream.rb +++ b/lib/gitlab/ci/trace/stream.rb @@ -4,7 +4,7 @@ module Gitlab # This was inspired from: http://stackoverflow.com/a/10219411/1520132 class Stream BUFFER_SIZE = 4096 - LIMIT_SIZE = 50.kilobytes + LIMIT_SIZE = 500.kilobytes attr_reader :stream @@ -14,14 +14,7 @@ module Gitlab def initialize @stream = yield - if @stream - @stream.binmode - # Ci::Ansi2html::Converter would read from @stream directly, - # using @stream.each_line to be specific. It's safe to set - # the encoding here because IO#seek(bytes) and IO#read(bytes) - # are not characters based, so encoding doesn't matter to them. - @stream.set_encoding(Encoding.default_external) - end + @stream&.binmode end def valid? @@ -68,8 +61,8 @@ module Gitlab def html(last_lines: nil) text = raw(last_lines: last_lines) - stream = StringIO.new(text) - ::Ci::Ansi2html.convert(stream).html + buffer = StringIO.new(text) + ::Ci::Ansi2html.convert(buffer).html end def extract_coverage(regex) diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb new file mode 100644 index 00000000000..c1f6b0cce3b --- /dev/null +++ b/spec/features/projects/wiki/shortcuts_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +feature 'Wiki shortcuts', :feature, :js do + let(:user) { create(:user) } + let(:project) { create(:empty_project, namespace: user.namespace) } + let(:wiki_page) do + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + end + + before do + login_as(user) + visit namespace_project_wiki_path(project.namespace, project, wiki_page) + end + + scenario 'Visit edit wiki page using "e" keyboard shortcut' do + find('body').native.send_key('e') + + expect(find('.wiki-page-title')).to have_content('Edit Page') + end +end diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index a9d4c6ef76f..24a2da9f6b6 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -107,4 +107,44 @@ describe('List model', () => { expect(gl.boardService.moveIssue) .toHaveBeenCalledWith(issue.id, list.id, listDup.id, undefined, undefined); }); + + describe('page number', () => { + beforeEach(() => { + spyOn(list, 'getIssues'); + }); + + it('increase page number if current issue count is more than the page size', () => { + for (let i = 0; i < 30; i += 1) { + list.issues.push(new ListIssue({ + title: 'Testing', + iid: _.random(10000) + i, + confidential: false, + labels: [list.label] + })); + } + list.issuesSize = 50; + + expect(list.issues.length).toBe(30); + + list.nextPage(); + + expect(list.page).toBe(2); + expect(list.getIssues).toHaveBeenCalled(); + }); + + it('does not increase page number if issue count is less than the page size', () => { + list.issues.push(new ListIssue({ + title: 'Testing', + iid: _.random(10000), + confidential: false, + labels: [list.label] + })); + list.issuesSize = 2; + + list.nextPage(); + + expect(list.page).toBe(1); + expect(list.getIssues).toHaveBeenCalled(); + }); + }); }); diff --git a/spec/javascripts/build_spec.js b/spec/javascripts/build_spec.js index 7174bf1e041..8ec96bdb583 100644 --- a/spec/javascripts/build_spec.js +++ b/spec/javascripts/build_spec.js @@ -1,11 +1,11 @@ /* eslint-disable no-new */ /* global Build */ - -require('~/lib/utils/datetime_utility'); -require('~/lib/utils/url_utility'); -require('~/build'); -require('~/breakpoints'); -require('vendor/jquery.nicescroll'); +import { bytesToKiB } from '~/lib/utils/number_utils'; +import '~/lib/utils/datetime_utility'; +import '~/lib/utils/url_utility'; +import '~/build'; +import '~/breakpoints'; +import 'vendor/jquery.nicescroll'; describe('Build', () => { const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/builds/1`; @@ -144,24 +144,6 @@ describe('Build', () => { expect($('#build-trace .js-build-output').text()).toMatch(/Different/); }); - it('shows information about truncated log', () => { - jasmine.clock().tick(4001); - const [{ success }] = $.ajax.calls.argsFor(0); - - success.call($, { - html: '<span>Update</span>', - status: 'success', - append: false, - truncated: true, - size: '50', - }); - - expect( - $('#build-trace .js-truncated-info').text().trim(), - ).toContain('Showing last 50 KiB of log'); - expect($('#build-trace .js-truncated-info-size').text()).toMatch('50'); - }); - it('reloads the page when the build is done', () => { spyOn(gl.utils, 'visitUrl'); @@ -176,6 +158,107 @@ describe('Build', () => { expect(gl.utils.visitUrl).toHaveBeenCalledWith(BUILD_URL); }); + + describe('truncated information', () => { + describe('when size is less than total', () => { + it('shows information about truncated log', () => { + jasmine.clock().tick(4001); + const [{ success }] = $.ajax.calls.argsFor(0); + + success.call($, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 50, + total: 100, + }); + + expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden'); + }); + + it('shows the size in KiB', () => { + jasmine.clock().tick(4001); + const [{ success }] = $.ajax.calls.argsFor(0); + const size = 50; + + success.call($, { + html: '<span>Update</span>', + status: 'success', + append: false, + size, + total: 100, + }); + + expect( + document.querySelector('.js-truncated-info-size').textContent.trim(), + ).toEqual(`${bytesToKiB(size)}`); + }); + + it('shows incremented size', () => { + jasmine.clock().tick(4001); + let args = $.ajax.calls.argsFor(0)[0]; + args.success.call($, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 50, + total: 100, + }); + + expect( + document.querySelector('.js-truncated-info-size').textContent.trim(), + ).toEqual(`${bytesToKiB(50)}`); + + jasmine.clock().tick(4001); + args = $.ajax.calls.argsFor(2)[0]; + args.success.call($, { + html: '<span>Update</span>', + status: 'success', + append: true, + size: 10, + total: 100, + }); + + expect( + document.querySelector('.js-truncated-info-size').textContent.trim(), + ).toEqual(`${bytesToKiB(60)}`); + }); + + it('renders the raw link', () => { + jasmine.clock().tick(4001); + const [{ success }] = $.ajax.calls.argsFor(0); + + success.call($, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 50, + total: 100, + }); + + expect( + document.querySelector('.js-raw-link').textContent.trim(), + ).toContain('Complete Raw'); + }); + }); + + describe('when size is equal than total', () => { + it('does not show the trunctated information', () => { + jasmine.clock().tick(4001); + const [{ success }] = $.ajax.calls.argsFor(0); + + success.call($, { + html: '<span>Update</span>', + status: 'success', + append: false, + size: 100, + total: 100, + }); + + expect(document.querySelector('.js-truncated-info').classList).toContain('hidden'); + }); + }); + }); }); }); }); diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js index 5fde8be9123..90b12c9f115 100644 --- a/spec/javascripts/lib/utils/number_utility_spec.js +++ b/spec/javascripts/lib/utils/number_utility_spec.js @@ -1,4 +1,4 @@ -import { formatRelevantDigits } from '~/lib/utils/number_utils'; +import { formatRelevantDigits, bytesToKiB } from '~/lib/utils/number_utils'; describe('Number Utils', () => { describe('formatRelevantDigits', () => { @@ -38,4 +38,11 @@ describe('Number Utils', () => { expect(leftFromDecimal.length).toBe(3); }); }); + + describe('bytesToKiB', () => { + it('calculates KiB for the given bytes', () => { + expect(bytesToKiB(1024)).toEqual(1); + expect(bytesToKiB(1000)).toEqual(0.9765625); + }); + }); }); diff --git a/spec/javascripts/user_callout_spec.js b/spec/javascripts/user_callout_spec.js index c0375ebc61c..28d0c7dcd99 100644 --- a/spec/javascripts/user_callout_spec.js +++ b/spec/javascripts/user_callout_spec.js @@ -14,7 +14,6 @@ describe('UserCallout', function () { this.userCallout = new UserCallout(); this.closeButton = $('.js-close-callout.close'); this.userCalloutBtn = $('.js-close-callout:not(.close)'); - this.userCalloutContainer = $('.user-callout'); }); it('hides when user clicks on the dismiss-icon', (done) => { diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index 03f040f4465..40ac5a3ed37 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -71,12 +71,20 @@ describe Gitlab::Ci::Trace::Stream do end describe '#append' do + let(:tempfile) { Tempfile.new } + let(:stream) do described_class.new do - StringIO.new("12345678") + tempfile.write("12345678") + tempfile.rewind + tempfile end end + after do + tempfile.unlink + end + it "truncates and append content" do stream.append("89", 4) stream.seek(0) @@ -84,6 +92,17 @@ describe Gitlab::Ci::Trace::Stream do expect(stream.size).to eq(6) expect(stream.raw).to eq("123489") end + + it 'appends in binary mode' do + '😺'.force_encoding('ASCII-8BIT').each_char.with_index do |byte, offset| + stream.append(byte, offset) + end + + stream.seek(0) + + expect(stream.size).to eq(4) + expect(stream.raw).to eq('😺') + end end describe '#set' do |