From 43f8be381e7e875894e0c2211094b7d20dc81e61 Mon Sep 17 00:00:00 2001 From: Jacob Fugal Date: Wed, 1 Apr 2015 15:15:08 -0600 Subject: [PATCH] prefer tDateToString/tTimeToString over strftime refs CNVS-19516 the {{strftime}} handlebars helper requires use of a hard-coded format string that's locale-unaware. tDateToString and tTimeToString take localization keys so that you get a format string appropriate to the locale. the remaining calls to {{strftime}} are for fixed-format machine-oriented strings, where they shouldn't be localized. this is an appropriate use, but really the only time {{strftime}} should be used. additionally, for all but the calendar agenda view, these should have been using fudged values ({{strftime}}, {{tTimeToString}} and {{tDateToString}} all expect fudged dates as input). fix that, too, while we're in here. finally, while adding specs, found that Date.parse doesn't like milliseconds, which meant dates weren't actually happening in the assignment-related specs; fixed that by switching to tz.parse. Since the values being parsed were explicitly UTC, this doesn't change the parsing semantics (e.g. fudged vs. not) test-plan: - set Norwegian as your locale [edit assignment dialog] - create an assignment with multiple due dates - go back to the assignment list, and choose the edit dialog from the cog menu - the tool-tip on the "Due" field should include the multiple dates formatted as " " rather than " " - also check same field for timezone consistency when profile timezone does not match browser timezone [assignment page] - for the same assignment, click through to the assignment's page - the due dates should display as " " rather than " " - also check same field for timezone consistency when profile timezone does not match browser timezone [availability column in assignment list] - for the same assignment - make one due date have an unlock at in the past and lock at in the future - make another due date have an unlock at in the future - view the assignment list - the tool-tip on "Multiple Dates" next to "Available" should have " " instead of " " for both the "Available until" (first section) value and the "Not available until" (second section) value - also check same field for timezone consistency when profile timezone does not match browser timezone [calendar agenda view] - go to the calendar agenda view - the due date times for future assignments should have 24-hour formatting, not 12-hour - the event dates for future non-assignment events should also have 24-hour formatting - also check same fields for timezone consistency when profile timezone does not match browser timezone [feature flags] - go to the feature flags pane in course settings - the "Estimated Release" for "New Quiz Statistics Page" should format " ", not " , " Change-Id: I0226cc14adcd39eceaa0e5f4bf66e0eefdc0bbf3 Reviewed-on: https://gerrit.instructure.com/51448 Reviewed-by: Cody Cutrer Reviewed-by: Jeremy Stanley Reviewed-by: Jonathan Featherstone Reviewed-by: John Corrigan Tested-by: Jenkins QA-Review: August Thornton Product-Review: Jacob Fugal --- app/coffeescripts/models/DateGroup.coffee | 13 ++-- .../assignments/CreateAssignment.handlebars | 2 +- .../assignments/DateDueColumnView.handlebars | 2 +- .../_available_date_description.handlebars | 4 +- app/views/jst/calendar/agendaView.handlebars | 4 +- .../jst/feature_flags/featureFlag.handlebars | 2 +- .../calendar/AgendaViewSpec.coffee | 62 +++++++++++++++++- .../AssignmentListItemViewSpec.coffee | 63 ++++++++++++++++++- .../CreateAssignmentViewSpec.coffee | 28 ++++++++- .../feature_flags/FeatureFlagViewSpec.coffee | 58 +++++++++++++++++ 10 files changed, 222 insertions(+), 16 deletions(-) create mode 100644 spec/coffeescripts/views/feature_flags/FeatureFlagViewSpec.coffee diff --git a/app/coffeescripts/models/DateGroup.coffee b/app/coffeescripts/models/DateGroup.coffee index 21b55114a2f..3732b54ed46 100644 --- a/app/coffeescripts/models/DateGroup.coffee +++ b/app/coffeescripts/models/DateGroup.coffee @@ -3,7 +3,8 @@ define [ 'underscore' 'jquery' 'i18n!assignments' -], (Backbone, _, $, I18n) -> + 'timezone' +], (Backbone, _, $, I18n, tz) -> class DateGroup extends Backbone.Model @@ -15,19 +16,19 @@ define [ dueAt: -> dueAt = @get("due_at") - if dueAt then Date.parse(dueAt) else null + if dueAt then tz.parse(dueAt) else null unlockAt: -> unlockAt = @get("unlock_at") - if unlockAt then Date.parse(unlockAt) else null + if unlockAt then tz.parse(unlockAt) else null lockAt: -> lockAt = @get("lock_at") - if lockAt then Date.parse(lockAt) else null + if lockAt then tz.parse(lockAt) else null now: -> now = @get("now") - if now then Date.parse(now) else new Date() + if now then tz.parse(now) else new Date() # no lock/unlock dates @@ -61,4 +62,4 @@ define [ available: @available() pending: @pending() open: @open() - closed: @closed() \ No newline at end of file + closed: @closed() diff --git a/app/views/jst/assignments/CreateAssignment.handlebars b/app/views/jst/assignments/CreateAssignment.handlebars index 91f9164a3ac..263a1e4e6f9 100644 --- a/app/views/jst/assignments/CreateAssignment.handlebars +++ b/app/views/jst/assignments/CreateAssignment.handlebars @@ -57,7 +57,7 @@ {{#each allDates}}
{{dueFor}}
-
{{#if dueAt}} {{strftime dueAt "%b %-d"}} {{else}} - {{/if}}
+
{{#if dueAt}} {{tDateToString (fudge dueAt) 'short'}} {{else}} - {{/if}}
{{/each}} diff --git a/app/views/jst/assignments/DateDueColumnView.handlebars b/app/views/jst/assignments/DateDueColumnView.handlebars index 62d85a2cd2a..d65f8ddeadf 100644 --- a/app/views/jst/assignments/DateDueColumnView.handlebars +++ b/app/views/jst/assignments/DateDueColumnView.handlebars @@ -15,7 +15,7 @@
{{#if dueAt}} - {{strftime dueAt "%b %-d"}} + {{tDateToString (fudge dueAt) 'short'}} {{else}} - diff --git a/app/views/jst/assignments/_available_date_description.handlebars b/app/views/jst/assignments/_available_date_description.handlebars index 5ca814fe642..ee0c7324291 100644 --- a/app/views/jst/assignments/_available_date_description.handlebars +++ b/app/views/jst/assignments/_available_date_description.handlebars @@ -1,14 +1,14 @@ {{#if pending}} {{#t "not_available_until"}}Not available until{{/t}} - {{strftime unlockAt "%b %-d"}} + {{tDateToString (fudge unlockAt) 'short'}} {{/if}} {{#if open}} {{#t "available_until"}}Available until{{/t}} - {{strftime lockAt "%b %-d"}} + {{tDateToString (fudge lockAt) 'short'}} {{/if}} diff --git a/app/views/jst/calendar/agendaView.handlebars b/app/views/jst/calendar/agendaView.handlebars index b0d09ad43bc..a1ff418079d 100644 --- a/app/views/jst/calendar/agendaView.handlebars +++ b/app/views/jst/calendar/agendaView.handlebars @@ -23,11 +23,11 @@
{{#if assignment}} {{#t "due"}}Due{{/t}}, - {{strftime originalStart "%l:%M%P"}} + {{tTimeToString originalStart "tiny"}} {{else}} {{#unless all_day}} {{#t "starts_at"}}Starts at{{/t}}, - {{strftime originalStart "%l:%M%P"}} + {{tTimeToString originalStart "tiny"}} {{/unless}} {{/if}}
diff --git a/app/views/jst/feature_flags/featureFlag.handlebars b/app/views/jst/feature_flags/featureFlag.handlebars index a1cd7325f30..8aca8168424 100644 --- a/app/views/jst/feature_flags/featureFlag.handlebars +++ b/app/views/jst/feature_flags/featureFlag.handlebars @@ -22,7 +22,7 @@ {{#if releaseOn}} {{#t "estimated_release"}}Estimated Release:{{/t}} - {{strftime releaseOn "%B %-d, %Y"}} + {{tDateToString (fudge releaseOn) "medium"}} {{/if}} diff --git a/spec/coffeescripts/calendar/AgendaViewSpec.coffee b/spec/coffeescripts/calendar/AgendaViewSpec.coffee index 80eb68e2921..34d622669ce 100644 --- a/spec/coffeescripts/calendar/AgendaViewSpec.coffee +++ b/spec/coffeescripts/calendar/AgendaViewSpec.coffee @@ -3,11 +3,14 @@ define [ 'underscore' 'timezone' 'vendor/timezone/America/Denver' + 'vendor/timezone/America/Juneau' + 'vendor/timezone/fr_FR' 'compiled/views/calendar/AgendaView' 'compiled/calendar/EventDataSource' 'helpers/ajax_mocks/api/v1/calendarEvents' 'helpers/ajax_mocks/api/v1/calendarAssignments' -], ($, _, tz, denver, AgendaView, EventDataSource, eventResponse, assignmentResponse) -> + 'helpers/I18nStubber' +], ($, _, tz, denver, juneau, french, AgendaView, EventDataSource, eventResponse, assignmentResponse, I18nStubber) -> loadEventPage = (server, includeNext = false) -> sendCustomEvents(server, eventResponse, assignmentResponse, includeNext) @@ -28,11 +31,13 @@ define [ @server = sinon.fakeServer.create() @snapshot = tz.snapshot() tz.changeZone(denver, 'America/Denver') + I18nStubber.pushFrame() teardown: -> @container.remove() @server.restore() tz.restore(@snapshot) + I18nStubber.popFrame() test 'should render results', -> view = new AgendaView(el: @container, dataSource: @dataSource) @@ -57,6 +62,11 @@ define [ ok @container.find('.agenda-load-btn').length test 'toJSON should properly serialize results', -> + I18nStubber.stub 'en', + 'date.formats.short_with_weekday': '%a, %b %-d' + 'date.abbr_day_names.1': 'Mon' + 'date.abbr_month_names.10': 'Oct' + view = new AgendaView(el: @container, dataSource: @dataSource) view.fetch(@contextCodes, @startDate) loadEventPage(@server) @@ -101,3 +111,53 @@ define [ sendCustomEvents(@server, JSON.stringify(events), JSON.stringify([]), false, 2) ok @container.find('.ig-row').length == 60, 'finds 60 ig-rows' + + test 'renders non-assignment events with locale-appropriate format string', -> + tz.changeLocale(french, 'fr_FR') + I18nStubber.setLocale 'fr_FR' + I18nStubber.stub 'fr_FR', 'time.formats.tiny': '%k:%M' + + view = new AgendaView(el: @container, dataSource: @dataSource) + view.fetch(@contextCodes, @startDate) + loadEventPage(@server) + + # this event has a start_at of 2013-10-08T20:30:00Z, or 1pm MDT + ok @container.find('.ig-details').slice(2, 3).text().match(/13:00/), 'formats according to locale' + + test 'renders assignment events with locale-appropriate format string', -> + tz.changeLocale(french, 'fr_FR') + I18nStubber.setLocale 'fr_FR' + I18nStubber.stub 'fr_FR', 'time.formats.tiny': '%k:%M' + + view = new AgendaView(el: @container, dataSource: @dataSource) + view.fetch(@contextCodes, @startDate) + loadEventPage(@server) + + # this event has a start_at of 2013-10-13T05:59:59Z, or 11:59pm MDT + ok @container.find('.ig-details').slice(12, 13).text().match(/23:59/), 'formats according to locale' + + test 'renders non-assignment events in appropriate timezone', -> + tz.changeZone(juneau, 'America/Juneau') + I18nStubber.stub 'en', + 'time.formats.tiny': '%l:%M%P' + 'date': {} + + view = new AgendaView(el: @container, dataSource: @dataSource) + view.fetch(@contextCodes, @startDate) + loadEventPage(@server) + + # this event has a start_at of 2013-10-08T20:30:00Z, or 11:00am AKDT + ok @container.find('.ig-details').slice(2, 3).text().match(/11:00am/), 'formats in correct timezone' + + test 'renders assignment events in appropriate timezone', -> + tz.changeZone(juneau, 'America/Juneau') + I18nStubber.stub 'en', + 'time.formats.tiny': '%l:%M%P' + 'date': {} + + view = new AgendaView(el: @container, dataSource: @dataSource) + view.fetch(@contextCodes, @startDate) + loadEventPage(@server) + + # this event has a start_at of 2013-10-13T05:59:59Z, or 9:59pm AKDT + ok @container.find('.ig-details').slice(12, 13).text().match(/9:59pm/), 'formats in correct timezone' diff --git a/spec/coffeescripts/views/assignments/AssignmentListItemViewSpec.coffee b/spec/coffeescripts/views/assignments/AssignmentListItemViewSpec.coffee index 1a1b653f610..9ec31bd1d18 100644 --- a/spec/coffeescripts/views/assignments/AssignmentListItemViewSpec.coffee +++ b/spec/coffeescripts/views/assignments/AssignmentListItemViewSpec.coffee @@ -4,9 +4,13 @@ define [ 'compiled/models/Submission' 'compiled/views/assignments/AssignmentListItemView' 'jquery' + 'timezone' + 'vendor/timezone/America/Juneau' + 'vendor/timezone/fr_FR' + 'helpers/I18nStubber' 'helpers/fakeENV' 'helpers/jquery.simulate' -], (Backbone, Assignment, Submission, AssignmentListItemView, $, fakeENV) -> +], (Backbone, Assignment, Submission, AssignmentListItemView, $, tz, juneau, french, I18nStubber, fakeENV) -> screenreaderText = null nonScreenreaderText = null @@ -144,9 +148,15 @@ define [ setup: -> genSetup.call @ + @snapshot = tz.snapshot() + I18nStubber.pushFrame() + teardown: -> genTeardown.call @ + tz.restore(@snapshot) + I18nStubber.popFrame() + test "initializes child views if can manage", -> view = createView(@model, canManage: true) ok view.publishIconView @@ -310,6 +320,57 @@ define [ equal spy.callCount, 0 AssignmentListItemView.prototype.updateScore.restore() + test "renders lockAt/unlockAt with locale-appropriate format string", -> + tz.changeLocale(french, 'fr_FR') + I18nStubber.setLocale 'fr_FR' + I18nStubber.stub 'fr_FR', + 'date.formats.short': '%-d %b' + 'date.abbr_month_names.8': 'août' + model = new AssignmentCollection([buildAssignment + id: 1 + all_dates: [ + { lock_at: "2113-08-28T04:00:00Z", title: "Summer Session" } + { unlock_at: "2113-08-28T04:00:00Z", title: "Winter Session" }]]).at(0) + + view = createView(model, canManage: true) + $dds = view.dateAvailableColumnView.$("#vdd_tooltip_#{@model.id}_lock div") + equal $("span", $dds.first()).last().text().trim(), '28 août' + equal $("span", $dds.last()).last().text().trim(), '28 août' + + test "renders lockAt/unlockAt in appropriate time zone", -> + tz.changeZone(juneau, 'America/Juneau') + I18nStubber.stub 'en', + 'date.formats.short': '%b %-d' + 'date.abbr_month_names.8': 'Aug' + + model = new AssignmentCollection([buildAssignment + id: 1 + all_dates: [ + { lock_at: "2113-08-28T04:00:00Z", title: "Summer Session" } + { unlock_at: "2113-08-28T04:00:00Z", title: "Winter Session" }]]).at(0) + + view = createView(model, canManage: true) + $dds = view.dateAvailableColumnView.$("#vdd_tooltip_#{@model.id}_lock div") + equal $("span", $dds.first()).last().text().trim(), 'Aug 27' + equal $("span", $dds.last()).last().text().trim(), 'Aug 27' + + test "renders due date column with locale-appropriate format string", -> + tz.changeLocale(french, 'fr_FR') + I18nStubber.setLocale 'fr_FR' + I18nStubber.stub 'fr_FR', + 'date.formats.short': '%-d %b' + 'date.abbr_month_names.8': 'août' + view = createView(@model, canManage: true) + equal view.dateDueColumnView.$("#vdd_tooltip_#{@model.id}_due div dd").first().text().trim(), '29 août' + + test "renders due date column in appropriate time zone", -> + tz.changeZone(juneau, 'America/Juneau') + I18nStubber.stub 'en', + 'date.formats.short': '%b %-d' + 'date.abbr_month_names.8': 'Aug' + view = createView(@model, canManage: true) + equal view.dateDueColumnView.$("#vdd_tooltip_#{@model.id}_due div dd").first().text().trim(), 'Aug 28' + module 'AssignmentListItemViewSpec—alternate grading type: percent', setup: -> genSetup.call @, assignment_grade_percent() diff --git a/spec/coffeescripts/views/assignments/CreateAssignmentViewSpec.coffee b/spec/coffeescripts/views/assignments/CreateAssignmentViewSpec.coffee index 1d484ff2cbf..b7e90f0ffd9 100644 --- a/spec/coffeescripts/views/assignments/CreateAssignmentViewSpec.coffee +++ b/spec/coffeescripts/views/assignments/CreateAssignmentViewSpec.coffee @@ -6,9 +6,13 @@ define [ 'compiled/views/assignments/CreateAssignmentView' 'compiled/views/DialogFormView' 'jquery' + 'timezone' + 'vendor/timezone/America/Juneau' + 'vendor/timezone/fr_FR' + 'helpers/I18nStubber' 'helpers/jquery.simulate' 'compiled/behaviors/tooltip' -], (Backbone, AssignmentGroupCollection, AssignmentGroup, Assignment, CreateAssignmentView, DialogFormView, $) -> +], (Backbone, AssignmentGroupCollection, AssignmentGroup, Assignment, CreateAssignmentView, DialogFormView, $, tz, juneau, french, I18nStubber) -> fixtures = $('#fixtures') @@ -113,12 +117,18 @@ define [ @assignment4 = assignment4() @group = assignmentGroup() + @snapshot = tz.snapshot() + I18nStubber.pushFrame() + teardown: -> ENV.VALID_DATE_RANGE = { start_at: {date: null, date_context: null} end_at: {date: null, date_context: null} } + tz.restore(@snapshot) + I18nStubber.popFrame() + test "initialize generates a new assignment for creation", -> view = createView(@group) equal view.model.get("assignment_group_id"), @group.get("id") @@ -297,3 +307,19 @@ define [ ok errors["due_at"] equal errors['due_at'][0]['message'], 'Due date cannot be before unlock date' + test "renders due dates with locale-appropriate format string", -> + tz.changeLocale(french, 'fr_FR') + I18nStubber.setLocale 'fr_FR' + I18nStubber.stub 'fr_FR', + 'date.formats.short': '%-d %b' + 'date.abbr_month_names.8': 'août' + view = createView(@assignment1) + equal view.$("#vdd_tooltip_assign_1 div dd").first().text().trim(), '28 août' + + test "renders due dates in appropriate time zone", -> + tz.changeZone(juneau, 'America/Juneau') + I18nStubber.stub 'en', + 'date.formats.short': '%b %-d' + 'date.abbr_month_names.8': 'Aug' + view = createView(@assignment1) + equal view.$("#vdd_tooltip_assign_1 div dd").first().text().trim(), 'Aug 27' diff --git a/spec/coffeescripts/views/feature_flags/FeatureFlagViewSpec.coffee b/spec/coffeescripts/views/feature_flags/FeatureFlagViewSpec.coffee new file mode 100644 index 00000000000..701b284b344 --- /dev/null +++ b/spec/coffeescripts/views/feature_flags/FeatureFlagViewSpec.coffee @@ -0,0 +1,58 @@ +define [ + 'compiled/views/feature_flags/FeatureFlagView' + 'compiled/models/FeatureFlag' + 'jquery' + 'timezone' + 'vendor/timezone/America/Juneau' + 'vendor/timezone/fr_FR' + 'helpers/I18nStubber' + 'helpers/fakeENV' +], (FeatureFlagView, FeatureFlag, $, tz, juneau, french, I18nStubber, fakeENV) -> + + module "FeatureFlagView", + setup: -> + @container = $('
', id: 'feature-flags').appendTo('#fixtures') + @snapshot = tz.snapshot() + I18nStubber.pushFrame() + fakeENV.setup() + + teardown: -> + @container.remove() + tz.restore(@snapshot) + I18nStubber.popFrame() + fakeENV.teardown() + + test 'should format release date with locale-appropriate format string', -> + releaseDate = tz.parse('2100-07-04T00:00:00Z') + + tz.changeLocale(french, 'fr_FR') + I18nStubber.setLocale 'fr_FR' + I18nStubber.stub 'fr_FR', + 'date.formats.medium': '%-d %b %Y' + 'date.abbr_month_names.7': 'juil.' + + flag = new FeatureFlag + releaseOn: releaseDate + feature_flag: + transitions: {} + view = new FeatureFlagView el: @container, model: flag + view.render() + + equal view.$('.feature-release-date').text().trim(), '4 juil. 2100' + + test 'should format release date in locale-appropriate format string', -> + releaseDate = tz.parse('2100-07-04T00:00:00Z') + + tz.changeZone(juneau, 'America/Juneau') + I18nStubber.stub 'en', + 'date.formats.medium': '%b %-d, %Y' + 'date.abbr_month_names.7': 'Jul' + + flag = new FeatureFlag + releaseOn: releaseDate + feature_flag: + transitions: {} + view = new FeatureFlagView el: @container, model: flag + view.render() + + equal view.$('.feature-release-date').text().trim(), 'Jul 3, 2100'