remove prior screenreader gradebook

Change-Id: I931fef060ecc64263fc1a8768deda505fef09fd6
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/354831
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Cameron Ray <cameron.ray@instructure.com>
QA-Review: Aaron Shafovaloff <ashafovaloff@instructure.com>
Product-Review: Aaron Shafovaloff <ashafovaloff@instructure.com>
This commit is contained in:
Aaron Shafovaloff 2024-08-12 14:22:30 -06:00
parent f0348ada41
commit 775df8e396
179 changed files with 15 additions and 55956 deletions

View File

@ -310,8 +310,7 @@ class GradebooksController < ApplicationController
# within the "gradebook" version there are two "views":
# "default"
# "learning_mastery"
# "individual" (also "srgb")
# "individual_enhanced"
# "individual"
if requested_gradebook_view.present?
if requested_gradebook_view != preferred_gradebook_view
@ -321,11 +320,8 @@ class GradebooksController < ApplicationController
return
end
individual_enhanced_enabled = @context.root_account.feature_enabled?(:individual_gradebook_enhancements)
if %w[srgb individual individual_enhanced].include?(gradebook_version) && individual_enhanced_enabled
if %w[srgb individual individual_enhanced].include?(gradebook_version)
show_enhanced_individual_gradebook
elsif ["srgb", "individual"].include?(gradebook_version)
show_individual_gradebook
elsif preferred_gradebook_view == "learning_mastery" && outcome_gradebook_enabled?
show_learning_mastery
else
@ -547,7 +543,6 @@ class GradebooksController < ApplicationController
grading_standard_scaling_factor: active_grading_standard_scaling_factor(grading_standard),
group_weighting_scheme: @context.group_weighting_scheme,
has_modules: @context.has_modules?,
individual_gradebook_enhancements: root_account.feature_enabled?(:individual_gradebook_enhancements),
late_policy: @context.late_policy.as_json(include_root: false),
login_handle_name: root_account.settings[:login_handle_name],
message_attachment_upload_folder_id: @current_user.conversation_attachments_folder.id.to_s,
@ -624,7 +619,6 @@ class GradebooksController < ApplicationController
grading_standard_points_based: active_grading_standard_points_based(grading_standard),
grading_standard_scaling_factor: active_grading_standard_scaling_factor(grading_standard),
group_weighting_scheme: @context.group_weighting_scheme,
individual_gradebook_enhancements: true,
outcome_gradebook_enabled: outcome_gradebook_enabled?,
outcome_rollups_url: api_v1_course_outcome_rollups_url(@context, per_page: 100),
proxy_submissions_allowed: Account.site_admin.feature_enabled?(:proxy_file_uploads) && @context.grants_right?(@current_user, session, :proxy_assignment_submission),
@ -723,7 +717,6 @@ class GradebooksController < ApplicationController
late_policy: @context.late_policy.as_json(include_root: false),
login_handle_name: root_account.settings[:login_handle_name],
has_modules: @context.has_modules?,
individual_gradebook_enhancements: root_account.feature_enabled?(:individual_gradebook_enhancements),
message_attachment_upload_folder_id: @current_user.conversation_attachments_folder.id.to_s,
outcome_gradebook_enabled: outcome_gradebook_enabled?,
outcome_links_url: api_v1_course_outcome_group_links_url(@context, outcome_style: :full),
@ -783,7 +776,6 @@ class GradebooksController < ApplicationController
settings: gradebook_settings(@context.global_id),
settings_update_url: api_v1_course_gradebook_settings_update_url(@context),
IMPROVED_LMGB: root_account.feature_enabled?(:improved_lmgb),
individual_gradebook_enhancements: root_account.feature_enabled?(:individual_gradebook_enhancements),
},
OUTCOME_AVERAGE_CALCULATION: root_account.feature_enabled?(:outcome_average_calculation),
outcome_service_results_to_canvas: outcome_service_results_to_canvas_enabled?,
@ -813,8 +805,7 @@ class GradebooksController < ApplicationController
COURSE_URL: named_context_url(@context, :context_url),
COURSE_IS_CONCLUDED: @context.is_a?(Course) && @context.completed?,
OUTCOME_GRADEBOOK_ENABLED: outcome_gradebook_enabled?,
OVERRIDE_GRADES_ENABLED: @context.try(:allow_final_grade_override?),
individual_gradebook_enhancements: @context.root_account.feature_enabled?(:individual_gradebook_enhancements)
OVERRIDE_GRADES_ENABLED: @context.try(:allow_final_grade_override?)
)
render html: "", layout: true

View File

@ -1,21 +0,0 @@
/*
* Copyright (C) 2014 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@import "base/environment";
@import "pages/shared/message_students";
@import "pages/screenreader_gradebook/screenreader_gradebook";

View File

@ -1,33 +0,0 @@
<%
# Copyright (C) 2011 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
%>
<%
css_bundle :screenreader_gradebook
deferred_js_bundle :screenreader_gradebook
%>
<% provide :page_title, t("Gradebook: Individual View") %>
<div>
<span data-component="GradebookSelector"></span>
<span data-component="IndividualProxyUploader" data-variant="IndividualGradebook"></span>
</div>
<div style="display:none;">
<%= render :partial => "shared/message_students" %>
</div>
<% if show_message_students_with_observers_dialog? %>
<span data-component="MessageStudentsWithObserversModal"></span>
<% end %>

View File

@ -1,9 +0,0 @@
{
"name": "canvas-lms",
"version": "0.0.0",
"private": true,
"dependencies": "These are all things that there is not an npm version of, so they have to stay here, even when using webpack",
"dependencies": {
"ember": "1.4.0"
}
}

View File

@ -125,7 +125,7 @@ it can work against the es6 source and not the compiled output.
[X] load up canvas w/ optimized js and RAILS_LOAD_ALL_LOCALES=1, switch your locale and ensure you see translations
[ ] extract duplicated loader code from i18nLinerHandlebars and emberHandlebars
[ ] extract duplicated loader code from i18nLinerHandlebars
[ ] in a separate commit extract i18nLinerHandlebars loader function and what
it duplicates from prepare_hbs to a function both can use.

View File

@ -286,7 +286,6 @@
"core-js-builder": "^3",
"css-loader": "^3",
"dependency-cruiser": "^16.3.1",
"ember-template-compiler": "^1.8.0",
"enzyme": "^3",
"enzyme-adapter-react-16": "^1.15.7",
"enzyme-to-json": "^3.3.4",

View File

@ -1,21 +0,0 @@
{
"name": "ember",
"version": "1.4.0",
"main": [
"./ember.js"
],
"dependencies": {
"jquery": ">= 1.7 <= 2.1",
"handlebars": ">= 1.0.0 < 2.0.0"
},
"homepage": "https://github.com/components/ember",
"_release": "1.4.0",
"_resolution": {
"type": "version",
"tag": "1.4.0",
"commit": "3d9e0e9401d60ddf8e703ccc7698916eb7d2c2cc"
},
"_source": "git://github.com/components/ember.git",
"_target": "1.4.0",
"_originalSource": "ember"
}

View File

@ -1 +0,0 @@
**/*

View File

@ -1,5 +0,0 @@
vendor
composer.lock
components
node_modules
emberjs

View File

@ -1 +0,0 @@
**/*

View File

@ -1,9 +0,0 @@
VERSION=v1.4.0
default:
@curl -O http://builds.emberjs.com/tags/$(VERSION)/ember.js
@curl -O http://builds.emberjs.com/tags/$(VERSION)/ember.min.js
@curl -O http://builds.emberjs.com/tags/$(VERSION)/ember.prod.js
@curl -O http://builds.emberjs.com/tags/$(VERSION)/ember-template-compiler.js
.PHONY: default

View File

@ -1,12 +0,0 @@
Ember.js
========
Shim repository for [Ember Application Framework](http://emberjs.com/).
This package provides the core of the ember.js framework.
Package Managers
----------------
* [Bower](http://bower.io): `ember`
* [Composer](http://packagist.org/packages/components/ember): `components/ember`

View File

@ -1,11 +0,0 @@
{
"name": "ember",
"version": "1.4.0",
"main": [
"./ember.js"
],
"dependencies": {
"jquery": ">= 1.7 <= 2.1",
"handlebars": ">= 1.0.0 < 2.0.0"
}
}

View File

@ -1,13 +0,0 @@
{
"name": "ember",
"repo": "components/ember",
"version": "1.4.0",
"main": "ember.js",
"scripts": [
"ember.js"
],
"dependencies": {
"component/jquery": ">= 1.7 <= 2.1",
"components/handlebars.js": ">= 1.0.0 < 2.0.0"
}
}

View File

@ -1,27 +0,0 @@
{
"name": "components/ember",
"description": "A framework for creating ambitious web applications.",
"type": "component",
"license": "MIT",
"require": {
"components/jquery": ">=1.7 <= 2.1",
"components/handlebars.js": "1.*"
},
"extra": {
"component": {
"scripts": [
"ember.js"
],
"files": [
"ember.min.js"
],
"shim": {
"exports": "Ember",
"deps": [
"jquery",
"handlebars"
]
}
}
}
}

View File

@ -1,324 +0,0 @@
(function() {
var Ember = { assert: function() {}, FEATURES: { isEnabled: function() {} } };
/*!
* @overview Ember - JavaScript Application Framework
* @copyright Copyright 2011-2014 Tilde Inc. and contributors
* Portions Copyright 2006-2011 Strobe Inc.
* Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/LICENSE
* @version 1.4.0
*/
(function() {
/**
@module ember
@submodule ember-handlebars-compiler
*/
// Eliminate dependency on any Ember to simplify precompilation workflow
var objectCreate = Object.create || function(parent) {
function F() {}
F.prototype = parent;
return new F();
};
var Handlebars = require('handlebars')
Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " +
"a SCRIPT tag in the HTML HEAD linking to the Handlebars file " +
"before you link to Ember.", Handlebars);
Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " +
"COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION +
" - Please note: Builds of master may have other COMPILER_REVISION values.",
Handlebars.COMPILER_REVISION === 4);
/**
Prepares the Handlebars templating library for use inside Ember's view
system.
The `Ember.Handlebars` object is the standard Handlebars library, extended to
use Ember's `get()` method instead of direct property access, which allows
computed properties to be used inside templates.
To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`.
This will return a function that can be used by `Ember.View` for rendering.
@class Handlebars
@namespace Ember
*/
Ember.Handlebars = objectCreate(Handlebars);
/**
Register a bound helper or custom view helper.
## Simple bound helper example
```javascript
Ember.Handlebars.helper('capitalize', function(value) {
return value.toUpperCase();
});
```
The above bound helper can be used inside of templates as follows:
```handlebars
{{capitalize name}}
```
In this case, when the `name` property of the template's context changes,
the rendered value of the helper will update to reflect this change.
For more examples of bound helpers, see documentation for
`Ember.Handlebars.registerBoundHelper`.
## Custom view helper example
Assuming a view subclass named `App.CalendarView` were defined, a helper
for rendering instances of this view could be registered as follows:
```javascript
Ember.Handlebars.helper('calendar', App.CalendarView):
```
The above bound helper can be used inside of templates as follows:
```handlebars
{{calendar}}
```
Which is functionally equivalent to:
```handlebars
{{view App.CalendarView}}
```
Options in the helper will be passed to the view in exactly the same
manner as with the `view` helper.
@method helper
@for Ember.Handlebars
@param {String} name
@param {Function|Ember.View} function or view class constructor
@param {String} dependentKeys*
*/
Ember.Handlebars.helper = function(name, value) {
Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Ember.Component.detect(value) || name.match(/-/));
if (Ember.View.detect(value)) {
Ember.Handlebars.registerHelper(name, Ember.Handlebars.makeViewHelper(value));
} else {
Ember.Handlebars.registerBoundHelper.apply(null, arguments);
}
};
/**
Returns a helper function that renders the provided ViewClass.
Used internally by Ember.Handlebars.helper and other methods
involving helper/component registration.
@private
@method helper
@for Ember.Handlebars
@param {Function} ViewClass view class constructor
*/
Ember.Handlebars.makeViewHelper = function(ViewClass) {
return function(options) {
Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2);
return Ember.Handlebars.helpers.view.call(this, ViewClass, options);
};
};
/**
@class helpers
@namespace Ember.Handlebars
*/
Ember.Handlebars.helpers = objectCreate(Handlebars.helpers);
/**
Override the the opcode compiler and JavaScript compiler for Handlebars.
@class Compiler
@namespace Ember.Handlebars
@private
@constructor
*/
Ember.Handlebars.Compiler = function() {};
// Handlebars.Compiler doesn't exist in runtime-only
if (Handlebars.Compiler) {
Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype);
}
Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler;
/**
@class JavaScriptCompiler
@namespace Ember.Handlebars
@private
@constructor
*/
Ember.Handlebars.JavaScriptCompiler = function() {};
// Handlebars.JavaScriptCompiler doesn't exist in runtime-only
if (Handlebars.JavaScriptCompiler) {
Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype);
Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler;
}
Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars";
Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() {
return "''";
};
/**
Override the default buffer for Ember Handlebars. By default, Handlebars
creates an empty String at the beginning of each invocation and appends to
it. Ember's Handlebars overrides this to append to a single shared buffer.
@private
@method appendToBuffer
@param string {String}
*/
Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
return "data.buffer.push("+string+");";
};
// Hacks ahead:
// Handlebars presently has a bug where the `blockHelperMissing` hook
// doesn't get passed the name of the missing helper name, but rather
// gets passed the value of that missing helper evaluated on the current
// context, which is most likely `undefined` and totally useless.
//
// So we alter the compiled template function to pass the name of the helper
// instead, as expected.
//
// This can go away once the following is closed:
// https://github.com/wycats/handlebars.js/issues/634
var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/,
BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/,
INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/;
Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) {
var helperInvocation = source[source.length - 1],
helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1],
matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation);
source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3];
}
var stringifyBlockHelperMissing = Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation;
var originalBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.blockValue;
Ember.Handlebars.JavaScriptCompiler.prototype.blockValue = function() {
originalBlockValue.apply(this, arguments);
stringifyBlockHelperMissing(this.source);
};
var originalAmbiguousBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue;
Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() {
originalAmbiguousBlockValue.apply(this, arguments);
stringifyBlockHelperMissing(this.source);
};
var prefix = "ember" + (+new Date()), incr = 1;
/**
Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that
all simple mustaches in Ember's Handlebars will also set up an observer to
keep the DOM up to date when the underlying property changes.
@private
@method mustache
@for Ember.Handlebars.Compiler
@param mustache
*/
Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
if (mustache.isHelper && mustache.id.string === 'control') {
mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]);
} else if (mustache.params.length || mustache.hash) {
// no changes required
} else {
var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]);
// Update the mustache node to include a hash value indicating whether the original node
// was escaped. This will allow us to properly escape values when the underlying value
// changes and we need to re-render the value.
if (!mustache.escaped) {
mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
}
mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
}
return Handlebars.Compiler.prototype.mustache.call(this, mustache);
};
/**
Used for precompilation of Ember Handlebars templates. This will not be used
during normal app execution.
@method precompile
@for Ember.Handlebars
@static
@param {String} string The template to precompile
*/
Ember.Handlebars.precompile = function(string) {
var ast = Handlebars.parse(string);
var options = {
knownHelpers: {
action: true,
unbound: true,
'bind-attr': true,
template: true,
view: true,
_triageMustache: true
},
data: true,
stringParams: true
};
var environment = new Ember.Handlebars.Compiler().compile(ast, options);
return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
};
// We don't support this for Handlebars runtime-only
if (Handlebars.compile) {
/**
The entry point for Ember Handlebars. This replaces the default
`Handlebars.compile` and turns on template-local data and String
parameters.
@method compile
@for Ember.Handlebars
@static
@param {String} string The template to compile
@return {Function}
*/
Ember.Handlebars.compile = function(string) {
var ast = Handlebars.parse(string);
var options = { data: true, stringParams: true };
var environment = new Ember.Handlebars.Compiler().compile(ast, options);
var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
var template = Ember.Handlebars.template(templateSpec);
template.isMethod = false; //Make sure we don't wrap templates with ._super
return template;
};
}
})();
exports.precompile = Ember.Handlebars.precompile;
exports.EmberHandlebars = Ember.Handlebars;
})();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,218 +0,0 @@
import Ember from './ember.mjs'
export default Ember
export const $ = Ember.$
export const A = Ember.A
export const ActionHandler = Ember.ActionHandler
export const addBeforeObserver = Ember.addBeforeObserver
export const addListener = Ember.addListener
export const addObserver = Ember.addObserver
export const aliasMethod = Ember.aliasMethod
export const anyUnprocessedMixins = Ember.anyUnprocessedMixins
export const Application = Ember.Application
export const Array = Ember.Array
export const arrayComputed = Ember.arrayComputed
export const ArrayComputedProperty = Ember.ArrayComputedProperty
export const ArrayController = Ember.ArrayController
export const ArrayPolyfills = Ember.ArrayPolyfills
export const ArrayProxy = Ember.ArrayProxy
export const assert = Ember.assert
export const beforeObserver = Ember.beforeObserver
export const beforeObserversFor = Ember.beforeObserversFor
export const beginPropertyChanges = Ember.beginPropertyChanges
export const bind = Ember.bind
export const Binding = Ember.Binding
export const BOOTED = Ember.BOOTED
export const cacheFor = Ember.cacheFor
export const canInvoke = Ember.canInvoke
export const changeProperties = Ember.changeProperties
export const Checkbox = Ember.Checkbox
export const CollectionView = Ember.CollectionView
export const Comparable = Ember.Comparable
export const compare = Ember.compare
export const Component = Ember.Component
export const ComponentLookup = Ember.ComponentLookup
export const ComponentTemplateDeprecation = Ember.ComponentTemplateDeprecation
export const computed = Ember.computed
export const ComputedProperty = Ember.ComputedProperty
export const config = Ember.config
export const Container = Ember.Container
export const ContainerView = Ember.ContainerView
export const Controller = Ember.Controller
export const controllerFor = Ember.controllerFor
export const ControllerMixin = Ember.ControllerMixin
export const copy = Ember.copy
export const Copyable = Ember.Copyable
export const CoreObject = Ember.CoreObject
export const CoreView = Ember.CoreView
export const create = Ember.create
export const DAG = Ember.DAG
export const DataAdapter = Ember.DataAdapter
export const debug = Ember.debug
export const DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION
export const DefaultResolver = Ember.DefaultResolver
export const Deferred = Ember.Deferred
export const DeferredMixin = Ember.DeferredMixin
export const defineProperty = Ember.defineProperty
export const deprecate = Ember.deprecate
export const deprecateFunc = Ember.deprecateFunc
export const Descriptor = Ember.Descriptor
export const destroy = Ember.destroy
export const EachProxy = Ember.EachProxy
export const Em = Ember
export const empty = Ember.empty
export const EMPTY_META = Ember.EMPTY_META
export const endPropertyChanges = Ember.endPropertyChanges
export const Enumerable = Ember.Enumerable
export const EnumerableUtils = Ember.EnumerableUtils
export const ENV = Ember.ENV
export const Error = Ember.Error
export const EventDispatcher = Ember.EventDispatcher
export const Evented = Ember.Evented
export const expandProperties = Ember.expandProperties
export const EXTEND_PROTOTYPES = Ember.EXTEND_PROTOTYPES
export const FEATURES = Ember.FEATURES
export const finishChains = Ember.finishChains
export const flushPendingChains = Ember.flushPendingChains
export const Freezable = Ember.Freezable
export const FROZEN_ERROR = Ember.FROZEN_ERROR
export const generateController = Ember.generateController
export const generateControllerFactory = Ember.generateControllerFactory
export const generateGuid = Ember.generateGuid
export const get = Ember.get
export const getMeta = Ember.getMeta
export const getProperties = Ember.getProperties
export const getWithDefault = Ember.getWithDefault
export const GUID_KEY = Ember.GUID_KEY
export const GUID_PREFIX = Ember.GUID_PREFIX
export const guidFor = Ember.guidFor
export const Handlebars = Ember.Handlebars
export const handleErrors = Ember.handleErrors
export const HashLocation = Ember.HashLocation
export const hasListeners = Ember.hasListeners
export const HistoryLocation = Ember.HistoryLocation
export const immediateObserver = Ember.immediateObserver
export const inspect = Ember.inspect
export const instrument = Ember.instrument
export const Instrumentation = Ember.Instrumentation
export const IS_BINDING = Ember.IS_BINDING
export const isArray = Ember.isArray
export const isEmpty = Ember.isEmpty
export const isEqual = Ember.isEqual
export const isGlobalPath = Ember.isGlobalPath
export const isNamespace = Ember.isNamespace
export const isNone = Ember.isNone
export const isWatching = Ember.isWatching
export const K = Ember.K
export const keys = Ember.keys
export const libraries = Ember.libraries
export const LinkView = Ember.LinkView
export const listenersDiff = Ember.listenersDiff
export const listenersFor = Ember.listenersFor
export const listenersUnion = Ember.listenersUnion
export const Location = Ember.Location
export const LOG_BINDINGS = Ember.LOG_BINDINGS
export const LOG_STACKTRACE_ON_DEPRECATION = Ember.LOG_STACKTRACE_ON_DEPRECATION
export const LOG_VERSION = Ember.LOG_VERSION
export const Logger = Ember.Logger
export const lookup = Ember.lookup
export const makeArray = Ember.makeArray
export const MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION
export const Map = Ember.Map
export const MapWithDefault = Ember.MapWithDefault
export const merge = Ember.merge
export const meta = Ember.meta
export const META_DESC = Ember.META_DESC
export const META_KEY = Ember.META_KEY
export const MetamorphENV = Ember.MetamorphENV
export const metaPath = Ember.metaPath
export const mixin = Ember.mixin
export const Mixin = Ember.Mixin
export const MODEL_FACTORY_INJECTIONS = Ember.MODEL_FACTORY_INJECTIONS
export const MutableArray = Ember.MutableArray
export const MutableEnumerable = Ember.MutableEnumerable
export const NAME_KEY = Ember.NAME_KEY
export const Namespace = Ember.Namespace
export const NativeArray = Ember.NativeArray
export const none = Ember.none
export const NoneLocation = Ember.NoneLocation
export const normalizeTuple = Ember.normalizeTuple
export const Object = Ember.Object
export const ObjectController = Ember.ObjectController
export const ObjectProxy = Ember.ObjectProxy
export const Observable = Ember.Observable
export const observer = Ember.observer
export const observersFor = Ember.observersFor
export const on = Ember.on
export const onerror = Ember.onerror
export const oneWay = Ember.oneWay
export const onLoad = Ember.onLoad
export const ORDER_DEFINITION = Ember.ORDER_DEFINITION
export const ORDER_DEFINITION_MAPPING = Ember.ORDER_DEFINITION_MAPPING
export const OrderedSet = Ember.OrderedSet
export const overrideChains = Ember.overrideChains
export const platform = Ember.platform
export const PromiseProxyMixin = Ember.PromiseProxyMixin
export const propertyDidChange = Ember.propertyDidChange
export const propertyWillChange = Ember.propertyWillChange
export const reduceComputed = Ember.reduceComputed
export const ReduceComputedProperty = Ember.ReduceComputedProperty
export const removeBeforeObserver = Ember.removeBeforeObserver
export const removeChainWatcher = Ember.removeChainWatcher
export const removeListener = Ember.removeListener
export const removeObserver = Ember.removeObserver
export const RenderBuffer = Ember.RenderBuffer
export const required = Ember.required
export const rewatch = Ember.rewatch
export const Route = Ember.Route
export const Router = Ember.Router
export const RouterDSL = Ember.RouterDSL
export const RSVP = Ember.RSVP
export const run = Ember.run
export const runLoadHooks = Ember.runLoadHooks
export const Select = Ember.Select
export const SelectOptgroup = Ember.SelectOptgroup
export const SelectOption = Ember.SelectOption
export const sendEvent = Ember.sendEvent
export const set = Ember.set
export const Set = Ember.Set
export const setMeta = Ember.setMeta
export const setProperties = Ember.setProperties
export const SHIM_ES5 = Ember.SHIM_ES5
export const SortableMixin = Ember.SortableMixin
export const State = Ember.State
export const StateManager = Ember.StateManager
export const String = Ember.String
export const STRINGS = Ember.STRINGS
export const STRUCTURED_PROFILE = Ember.STRUCTURED_PROFILE
export const SubArray = Ember.SubArray
export const subscribe = Ember.subscribe
export const TargetActionSupport = Ember.TargetActionSupport
export const TEMPLATES = Ember.TEMPLATES
export const Test = Ember.Test
export const testing = Ember.testing
export const TESTING_DEPRECATION = Ember.TESTING_DEPRECATION
export const TextArea = Ember.TextArea
export const TextField = Ember.TextField
export const TextSupport = Ember.TextSupport
export const toString = Ember.toString
export const TrackedArray = Ember.TrackedArray
export const tryCatchFinally = Ember.tryCatchFinally
export const tryFinally = Ember.tryFinally
export const tryInvoke = Ember.tryInvoke
export const trySet = Ember.trySet
export const typeOf = Ember.typeOf
export const unwatch = Ember.unwatch
export const unwatchKey = Ember.unwatchKey
export const unwatchPath = Ember.unwatchPath
export const uuid = Ember.uuid
export const VERSION = Ember.VERSION
export const View = Ember.View
export const ViewTargetActionSupport = Ember.ViewTargetActionSupport
export const ViewUtils = Ember.ViewUtils
export const warn = Ember.warn
export const watch = Ember.watch
export const watchedEvents = Ember.watchedEvents
export const watchKey = Ember.watchKey
export const watchPath = Ember.watchPath
export const wrap = Ember.wrap

View File

@ -1,6 +0,0 @@
{
"name": "ember",
"version": "1.4.0",
"main": "./index.mjs",
"owner": "EVAL"
}

View File

@ -986,21 +986,6 @@ describe GradebooksController do
expect(response).to render_template("gradebooks/gradebook")
end
it "renders screenreader gradebook when preferred with 'individual'" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
@admin.set_preference(:gradebook_version, "individual")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/individual")
end
it "renders screenreader gradebook when preferred with 'srgb'" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
# most a11y users will have this set from before New Gradebook existed
@admin.set_preference(:gradebook_version, "srgb")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/individual")
end
it "renders default gradebook when user has no preference" do
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/gradebook")
@ -1013,34 +998,6 @@ describe GradebooksController do
expect(response).to render_template("gradebooks/gradebook")
end
it "renders enhanced individual gradebook when individual_enhanced & individual_gradebook_enhancements is enabled" do
@course.root_account.enable_feature!(:individual_gradebook_enhancements)
@admin.set_preference(:gradebook_version, "individual_enhanced")
get "show", params: { course_id: @course.id }
expect(response).to render_template("layouts/application")
end
it "renders enhanced individual gradebook when individual & individual_gradebook_enhancements is enabled" do
@course.root_account.enable_feature!(:individual_gradebook_enhancements)
@admin.set_preference(:gradebook_version, "individual")
get "show", params: { course_id: @course.id }
expect(response).to render_template("layouts/application")
end
it "renders enhanced individual gradebook when srgb & individual_gradebook_enhancements is enabled" do
@course.root_account.enable_feature!(:individual_gradebook_enhancements)
@admin.set_preference(:gradebook_version, "srgb")
get "show", params: { course_id: @course.id }
expect(response).to render_template("layouts/application")
end
it "renders traditional gradebook when individual_gradebook_enhancements is disabled" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
@admin.set_preference(:gradebook_version, "individual_enhanced")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/gradebook")
end
describe "score to ungraded" do
before do
options = Gradebook::ApplyScoreToUngradedSubmissions::Options.new(
@ -1118,21 +1075,6 @@ describe GradebooksController do
end
end
context "in development and requested version is 'individual'" do
before do
account_admin_user(account: @course.root_account)
user_session(@admin)
@admin.set_preference(:gradebook_version, "default")
allow(Rails.env).to receive(:development?).and_return(true)
end
it "renders screenreader gradebook" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
get "show", params: { course_id: @course.id, version: "individual" }
expect(response).to render_template("gradebooks/individual")
end
end
describe "prefetching" do
render_views
@ -1269,33 +1211,6 @@ describe GradebooksController do
end
end
describe "show_message_students_with_observers_dialog" do
shared_examples_for "environment variable" do
it "is true when the feature is enabled" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
Account.site_admin.enable_feature!(:message_observers_of_students_who)
get :show, params: { course_id: @course.id }
expect(gradebook_options[:show_message_students_with_observers_dialog]).to be true
end
it "is false when the feature is not enabled" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
get :show, params: { course_id: @course.id }
expect(gradebook_options[:show_message_students_with_observers_dialog]).to be false
end
end
context "when individual gradebook is enabled" do
before { @teacher.set_preference(:gradebook_version, "srgb") }
include_examples "environment variable"
end
context "when default gradebook is enabled" do
include_examples "environment variable"
end
end
describe "grading_standard" do
it "uses the Canvas default grading standard if the course does not have one" do
get :show, params: { course_id: @course.id }
@ -1691,13 +1606,6 @@ describe GradebooksController do
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebook")
end
it "redirects to Individual View with a friendly URL" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
@teacher.set_preference(:gradebook_version, "srgb")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/individual")
end
end
it "renders the unauthorized page without gradebook authorization" do
@ -1830,13 +1738,6 @@ describe GradebooksController do
expect(response).to render_template("gradebooks/gradebook")
end
it "renders 'individual' when the user uses individual view" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
update_preferred_gradebook_version!("individual")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/individual")
end
it "updates the user's preference when the requested view is 'gradebook'" do
get "show", params: { course_id: @course.id, view: "gradebook" }
@teacher.reload
@ -1889,13 +1790,6 @@ describe GradebooksController do
expect(response).to render_template("gradebooks/gradebook")
end
it "renders 'individual' when the user uses individual view" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
update_preferred_gradebook_version!("individual")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/individual")
end
it "redirects to the gradebook when requesting the preferred view" do
get "show", params: { course_id: @course.id, view: "gradebook" }
expect(response).to redirect_to(action: "show")
@ -1929,13 +1823,6 @@ describe GradebooksController do
expect(response).to render_template("gradebooks/learning_mastery")
end
it "renders 'individual' when the user uses individual view" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
update_preferred_gradebook_version!("individual")
get "show", params: { course_id: @course.id }
expect(response).to render_template("gradebooks/individual")
end
it "redirects to the gradebook when requesting the preferred view" do
get "show", params: { course_id: @course.id, view: "learning_mastery" }
expect(response).to redirect_to(action: "show")

View File

@ -51,32 +51,6 @@ describe "Final Grade Override" do
end
end
context "Individual Gradebook" do
before do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
@student = @students.first
@enrollment = @course.enrollments.find_by(user: @student)
@enrollment.scores.find_by(course_score: true).update!(override_score: 97.1)
user_session(@teacher)
SRGB.visit(@course.id)
SRGB.allow_final_grade_override_option.click
SRGB.select_student(@student)
end
it "display override percent in individual gradebook", priority: "1" do
expect(SRGB.final_grade_override.text).to include "97.1%"
end
it "display override grade in individual gradebook", priority: "1" do
expect(SRGB.final_grade_override_input).to have_value "A"
end
it "saves overridden grade in SRGB", priority: "1" do
SRGB.enter_override_grade("D-")
expect(@enrollment.scores.find_by(course_score: true).override_score).to be 61.0
end
end
it "displays the override column", priority: "1" do
user_session(@teacher)
Gradebook.visit(@course)

View File

@ -155,86 +155,6 @@ describe "Excuse an Assignment" do
end
shared_examples "Basic Behavior" do |view|
context "Group Assignments", :group do
it "preserves assignment excused status", priority: "1" do
course_with_teacher_logged_in
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
group_test_setup 4, 1, 1
@students.each { |student| @testgroup[0].add_user student }
@testgroup[0].save!
assignment = @course.assignments.create!(
title: "Group Assignment",
group_category_id: @group_category[0].id,
grade_group_students_individually: false,
points_possible: 20
)
assignment.grade_student @students[1], excuse: true, grader: @teacher
assignment.grade_student @students[0], grade: 15, grader: @teacher
score_values = []
if view == "srgb"
get "/courses/#{@course.id}/gradebook/change_gradebook_version?version=srgb"
click_option f("#assignment_select"), assignment.title
next_student = f(".student_navigation button.next_object")
4.times do
next_student.click
wait_for_ajaximations
score_values << f("#student_and_assignment_grade").attribute("value")
end
else
get "/courses/#{@course.id}/gradebook/"
wait_for_ajaximations
score_values = ff(".canvas_1 .slick-row .slick-cell:first-child").map(& :text)
end
expect(score_values).to eq %w[15 Excused 15 15]
end
it "excuses assignments on individual basis", priority: "1" do
course_with_teacher_logged_in
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
group_test_setup 2, 1, 1
@students.each { |student| @testgroup[0].add_user student }
@testgroup[0].save!
a1 = @course.assignments.create!(
title: "Group Assignment",
group_category_id: @group_category[0].id,
grade_group_students_individually: false,
points_possible: 10
)
a2 = @course.assignments.create! title: "Assignment", points_possible: 20
@students.each do |student|
a1.grade_student student, grade: 5, grader: @teacher
a2.grade_student student, grade: 20, grader: @teacher
end
a1.grade_student @students[1], excuse: true, grader: @teacher
totals = []
if view == "srgb"
get "/courses/#{@course.id}/gradebook/change_gradebook_version?version=srgb"
next_student = f(".student_navigation button.next_object")
2.times do
next_student.click
wait_for_ajaximations
totals << f("span.total-grade").text[/\d+(\.\d+)?%/]
end
else
get "/courses/#{@course.id}/gradebook/"
wait_for_ajaximations
totals = ff(".canvas_1 .slick-row .slick-cell:last-child").map(& :text)
end
expect(totals).to eq(["83.33%", "100%"]).or eq ["83.3%", "100%"]
end
end
it "formats excused grade like dropped assignment", priority: "1" do
assignment = @course.assignments.create! title: "Excuse Me", points_possible: 20

View File

@ -1,77 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2017 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require_relative "../pages/gradebook_individual_view_page"
require_relative "../../helpers/gradebook_common"
describe "Late Policies:" do
include_context "in-process server selenium tests"
include GradebookCommon
include_context "late_policy_course_setup"
before(:once) do
# create course with students, assignments, submissions and grades
init_course_with_students(1)
create_course_late_policy
create_assignments
make_submissions
grade_assignments
end
context "grade detail tray other" do
before do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
user_session(@teacher)
GradebookIndividualViewPage.visit(@course.id)
end
it "missing submission has missing pill removed after being graded" do
GradebookIndividualViewPage.select_assignment(@a2)
GradebookIndividualViewPage.select_student(@course.students.first)
expect(find_all_with_jquery(".missing-pill:contains('missing')").length).to eq 0
end
it "late submission has late pill" do
GradebookIndividualViewPage.select_assignment(@a1)
GradebookIndividualViewPage.select_student(@course.students.first)
expect(GradebookIndividualViewPage.status_pill("late")).to be_displayed
end
it "late submission has late penalty" do
GradebookIndividualViewPage.select_assignment(@a1)
GradebookIndividualViewPage.select_student(@course.students.first)
late_penalty_value = "-" + @course.students.first.submissions.find_by(assignment_id: @a1.id).points_deducted.to_s
# the data from rails and data from ui are not in the same format
expect(GradebookIndividualViewPage.late_penalty_text.to_f.to_s).to eq late_penalty_value
end
it "late submission has final grade" do
GradebookIndividualViewPage.select_assignment(@a1)
GradebookIndividualViewPage.select_student(@course.students.first)
final_grade_value = @course.students.first.submissions.find_by(assignment_id: @a1.id).published_grade
expect(GradebookIndividualViewPage.late_policy_final_grade_text).to eq final_grade_value
end
end
end

View File

@ -1,53 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2017 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require_relative "../pages/srgb_page"
require_relative "weighting_setup"
require_relative "a_gradebook_shared_example"
describe.skip "individual view EVAL-4360 remove test file with FF individual_gradebook_enhancements" do
include_context "in-process server selenium tests"
include WeightingSetup
let(:total_grade) do
user_session(@teacher)
grading_period_titles = ["All Grading Periods", @gp1.title, @gp2.title]
SRGB.visit(@course.id)
if @grading_period_index
SRGB.select_grading_period(grading_period_titles[@grading_period_index])
refresh_page
end
SRGB.select_student(@student)
SRGB.total_score
end
let(:individual_view) { true }
before(:once) do
weighted_grading_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
end
after do
clear_local_storage
end
it_behaves_like "a gradebook"
end

View File

@ -301,7 +301,7 @@ class EnhancedSRGB
end
def visit(course_id)
get "/courses/#{course_id}/gradebook/change_gradebook_version?version=individual_enhanced"
get "/courses/#{course_id}/gradebook/change_gradebook_version?version=individual"
end
def sort_assignments_by(sort_order)

View File

@ -1,161 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2018 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require_relative "../../helpers/gradebook_common"
require_relative "../pages/srgb_page"
require_relative "../pages/speedgrader_page"
describe "Individual View Gradebook" do
include_context "in-process server selenium tests"
include GradebookCommon
before(:once) do
# create a course with a teacher
@teacher1 = course_with_teacher(course_name: "Course1", active_all: true).user
# enroll a second teacher
@teacher2 = course_with_teacher(course: @course, name: "Teacher2", active_all: true).user
# enroll two students
@student1 = course_with_student(course: @course, name: "Student1", active_all: true).user
@student2 = course_with_student(course: @course, name: "Student2", active_all: true).user
end
context "with a moderated assignment" do
before(:once) do
# create moderated assignment
@moderated_assignment = @course.assignments.create!(
title: "Moderated Assignment1",
grader_count: 2,
final_grader_id: @teacher1.id,
grading_type: "points",
points_possible: 15,
submission_types: "online_text_entry",
moderated_grading: true
)
# give a grade as non-final grader
@student1_submission = @moderated_assignment.grade_student(@student1, grade: 13, grader: @teacher2, provisional: true).first
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
end
before do
# switch session to non-final-grader
user_session(@teacher2)
end
it "prevents unmuting the assignment before grades are posted", priority: "2" do
SRGB.visit(@course.id)
SRGB.select_assignment(@moderated_assignment)
wait_for_ajaximations
scroll_into_view("#assignment_muted_check")
expect(SRGB.assignment_muted_checkbox.attribute("checked")).to eq "true"
expect(SRGB.assignment_muted_checkbox.attribute("disabled")).to eq "true"
end
it "prevents grading for the assignment before grades are posted", priority: "2" do
SRGB.visit(@course.id)
SRGB.select_student(@student1)
SRGB.select_assignment(@moderated_assignment)
scroll_into_view("#student_and_assignment_grade")
expect(SRGB.main_grade_input.attribute("disabled")).to eq "true"
end
context "when grades are posted" do
before(:once) do
@moderated_assignment.update!(grades_published_at: Time.zone.now)
end
before do
SRGB.visit(@course.id)
end
it "allows grading for the assignment", priority: "2" do
SRGB.select_student(@student1)
SRGB.select_assignment(@moderated_assignment)
SRGB.enter_grade("15")
expect(SRGB.current_grade).to eq "15"
end
it "allows unmuting the assignment", priority: "2" do
SRGB.select_assignment(@moderated_assignment)
wait_for_ajaximations
scroll_into_view("#assignment_muted_check")
expect(SRGB.assignment_muted_checkbox.attribute("disabled")).to be_nil
end
end
end
context "with an anonymous assignment" do
before(:once) do
# create a new anonymous assignment
@anonymous_assignment = @course.assignments.create!(
title: "Anonymous Assignment",
submission_types: "online_text_entry",
anonymous_grading: true,
points_possible: 10
)
# create an unmuted anonymous assignment
@unmuted_anonymous_assignment = @course.assignments.create!(
title: "Unmuted Anon Assignment",
submission_types: "online_text_entry",
anonymous_grading: true,
points_possible: 10
)
@unmuted_anonymous_assignment.unmute!
end
before do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
user_session(@teacher1)
SRGB.visit(@course.id)
end
it "excludes the muted assignment from the assignment list", priority: "1" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.select_student(@student1)
SRGB.assignment_dropdown.click
# muted anonymous assignment is not displayed
expect(SRGB.assignment_dropdown).not_to include_text "Anonymous Assignment"
# unmuted anonymous assignment is displayed
expect(SRGB.assignment_dropdown).to include_text "Unmuted Anon Assignment"
end
it "hides student names in speedgrader", priority: "2" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
# Open speedgrader for the anonymous assignment
SRGB.select_assignment(@anonymous_assignment)
scroll_into_view("#assignment-speedgrader-link")
wait_for_new_page_load(SRGB.speedgrader_link.click)
# open the student list dropdown
Speedgrader.students_dropdown_button.click
# ensure the student names are anonymized
student_names = Speedgrader.students_select_menu_list.map(&:text)
expect(student_names).to match_array ["Student 1", "Student 2"]
end
end
end

View File

@ -1,70 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2016 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require_relative "../../helpers/gradebook_common"
require_relative "../pages/srgb_page"
describe.skip "Screenreader Gradebook EVAL-4360 remove test file with FF individual_gradebook_enhancements" do
include_context "in-process server selenium tests"
include_context "reusable_gradebook_course"
include GradebookCommon
let(:srgb_page) { SRGB }
let(:course_setup) do
enroll_teacher_and_students
assignment_1
assignment_2
student_submission
assignment_1.grade_student(student, grade: 10, grader: teacher)
end
before do
course_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
user_session(teacher)
srgb_page.visit(test_course.id)
srgb_page.select_student(student)
end
it "toggles ungraded as 0 with correct grades", priority: "2" do
srgb_page.select_assignment(assignment_1)
srgb_page.ungraded_as_zero.click
expect(srgb_page.final_grade).to include_text("50%")
srgb_page.ungraded_as_zero.click
expect(srgb_page.final_grade).to include_text("100%")
end
it "hides student names", priority: "2" do
srgb_page.hide_student_names.click
expect(srgb_page.secondary_id_label).to include_text("hidden")
end
it "shows conluded enrollments", priority: "2" do
srgb_page.concluded_enrollments.click
wait_for_ajaximations
expect(srgb_page.student_dropdown).to include_text("Student, Concluded")
end
it "shows notes in student info", priority: "2" do
srgb_page.show_notes_option.click
expect(srgb_page.notes_field).to be_present
end
end

View File

@ -1,292 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2016 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require_relative "../../helpers/gradebook_common"
require_relative "../pages/srgb_page"
require_relative "../setup/gradebook_setup"
require_relative "../pages/gradebook_grade_detail_tray_page"
describe.skip "Screenreader Gradebook grading EVAL-4360 remove test file with FF individual_gradebook_enhancements" do
include_context "in-process server selenium tests"
include_context "reusable_gradebook_course"
include GradebookCommon
include GradebookSetup
let(:srgb_page) { SRGB }
let(:course_setup) do
enroll_teacher_and_students
assignment_1
assignment_2
assignment_2.update!(due_at: 2.days.ago, submission_types: "online_text_entry")
assignment_3
assignment_4
student_submission
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
end
let(:login_to_srgb) do
user_session(teacher)
srgb_page.visit(test_course.id)
srgb_page.select_student(student)
end
let(:proxy_submit) do
assignment_1.update!(submission_types: "online_upload")
file_attachment = attachment_model(content_type: "application/pdf", context: student)
submission = assignment_1.submit_homework(student, submission_type: "online_upload", attachments: [file_attachment])
teacher.update!(short_name: "Test Teacher")
submission.update!(proxy_submitter: teacher)
end
let(:proxy_permission) do
Account.site_admin.enable_feature!(:proxy_file_uploads)
teacher_role = Role.get_built_in_role("TeacherEnrollment", root_account_id: Account.default.id)
RoleOverride.create!(
permission: "proxy_assignment_submission",
enabled: true,
role: teacher_role,
account: @course.root_account
)
end
context "in Grades section" do
before do
course_setup
login_to_srgb
end
it "displays correct Grade for label on assignments" do
srgb_page.select_assignment(assignment_1)
expect(srgb_page.grade_for_label).to include_text("Grade for User - Points Assignment")
end
it "displays correct Grade for: label on next assignment" do
srgb_page.select_assignment(assignment_1)
srgb_page.next_assignment_button.click
expect(srgb_page.grade_for_label).to include_text("Grade for User - Percent Assignment")
end
it "displays correct points for graded by Points" do
srgb_page.select_assignment(assignment_1)
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, 8)
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(srgb_page.main_grade_input).to have_value("8")
end
it "displays a flash message when an invalid grade is given and preserves the missing tag" do
srgb_page.select_assignment(assignment_2)
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, "Invalid Grade")
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(f(".missing-pill")).to be_displayed
expect_flash_message :error, "Invalid Grade"
end
it "does not grade student when tabing over the grade input without editing" do
srgb_page.select_assignment(assignment_2)
srgb_page.main_grade_input.click
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(f(".missing-pill")).to be_displayed
expect(srgb_page.main_grade_input).to have_value("-")
end
it "excuses student when grade input is 'Excused'" do
srgb_page.select_assignment(assignment_2)
srgb_page.main_grade_input.clear
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, "Excused")
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(srgb_page.excuse_checkbox.attribute("checked")).to be_truthy
expect(srgb_page.main_grade_input).to have_value("Excused")
end
it "displays correct points for graded by Percent" do
srgb_page.select_assignment(assignment_2)
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, 8)
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(srgb_page.main_grade_input).to have_value("80%")
end
it "displays correct points for graded by Complete/Incomplete" do
srgb_page.select_assignment(assignment_3)
click_option("#student_and_assignment_grade", "Complete")
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(f("#grading div.ember-view")).to include_text("10 out of 10")
end
it "displays correct points for graded by Letter Grade" do
srgb_page.select_assignment(assignment_4)
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, 8)
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
expect(srgb_page.main_grade_input).to have_value("B-")
end
it "displays submission details modal with correct grade" do
srgb_page.select_assignment(assignment_1)
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, 8)
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
srgb_page.submission_details_button.click
expect(f(".submission_details_dialog .assignment-name")).to include_text(assignment_1.name)
expect(fj(".submission_details_grade_form input:visible")).to have_value("8")
end
it "updates grade in submission details modal" do
skip("fragile")
srgb_page.select_assignment(assignment_2)
replace_content(srgb_page.main_grade_input, 8)
srgb_page.submission_details_button.click
# change grade from 8 to 10 in the assignment details modal
details_modal_grade_input = f(".submission_details_grade_form input")
details_modal_grade_input.clear
replace_content(details_modal_grade_input, 10)
f("form.submission_details_grade_form button").click
expect(srgb_page.main_grade_input).to have_value("100%")
end
end
context "displays warning" do
before do
course_setup
end
it "on late submissions" do
login_to_srgb
srgb_page.select_assignment(assignment_1)
expect(f(".late-pill")).to include_text("LATE")
expect(f(".submission_late_penalty")).to include_text("Late Penalty")
expect(f(".submission_final_grade")).to include_text("Final Grade")
end
it "on missing submissions" do
assignment_1.submissions.find_by(user: student).update!(late_policy_status: "missing")
login_to_srgb
srgb_page.select_assignment(assignment_1)
expect(f(".missing-pill")).to include_text("MISSING")
end
it "on dropped assignments" do
# create an assignment group with drop lowest 1 score rule
srgb_page.drop_lowest(test_course, 1)
# grade a few assignments with one really low grade
assignment_1.grade_student(student, grade: 3, grader: teacher)
assignment_2.grade_student(student, grade: 10, grader: teacher)
login_to_srgb
srgb_page.select_assignment(assignment_1)
# indicates assignment_1 was dropped
expect(f(".dropped.muted")).to include_text("This grade is currently dropped for this student.")
end
it "on resubmitted assignments" do
# grade assignment
assignment_1.grade_student(student, grade: 8, grader: teacher)
# resubmit as student
Timecop.travel(1.hour.from_now) do
assignment_1.submit_homework(
student,
submission_type: "online_text_entry",
body: "re-submitting!"
)
end
login_to_srgb
srgb_page.select_assignment(assignment_1)
# indicates assignment_1 was resubmitted
expect(f(".resubmitted.muted")).to include_text("This assignment has been resubmitted")
# grade the assignment again
srgb_page.grade_srgb_assignment(srgb_page.main_grade_input, 10)
srgb_page.tab_out_of_input(srgb_page.main_grade_input)
# warning should be removed
expect(f("#content")).not_to contain_css(".resubmitted.muted em")
end
end
context "with grading periods" do
before do
term_name = "First Term"
create_grading_periods(term_name)
add_teacher_and_student
associate_course_to_term(term_name)
user_session(@teacher)
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
end
it "assignment in ended gp should be gradable" do
assignment = @course.assignments.create!(due_at: 13.days.ago, title: "assign in ended")
SRGB.visit(@course.id)
SRGB.select_grading_period(@gp_ended.title)
SRGB.select_student(@student)
SRGB.select_assignment(assignment)
SRGB.enter_grade(8)
expect(SRGB.current_grade).to eq "8"
expect(Submission.where(assignment_id: assignment.id, user_id: @student.id).first.grade).to eq "8"
end
it "assignment in closed gp should not be gradable" do
assignment = @course.assignments.create!(due_at: 18.days.ago, title: "assign in closed")
SRGB.visit(@course.id)
SRGB.select_grading_period(@gp_closed.title)
SRGB.select_student(@student)
SRGB.select_assignment(assignment)
expect(SRGB.grading_enabled?).to be false
expect(Submission.not_placeholder.where(assignment_id: assignment.id, user_id: @student.id).first).to be_nil
end
end
context "submit for student" do
before do
enroll_teacher_and_students
proxy_permission
assignment_1.update!(submission_types: "online_upload")
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
end
it "displays submit for student button for file upload assignments" do
login_to_srgb
srgb_page.select_assignment(assignment_1)
expect(srgb_page.submit_for_student_button).to include_text("Submit for Student")
end
it "displays the submitter's identity for proxy submissions" do
proxy_submit
login_to_srgb
srgb_page.select_assignment(assignment_1)
expect(srgb_page.proxy_submitter_label).to include_text("Submitted by Test Teacher")
end
end
end

View File

@ -1,468 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2014 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require_relative "../../helpers/gradebook_common"
require_relative "../pages/gradebook_cells_page"
require_relative "../pages/srgb_page"
require_relative "../pages/grading_curve_page"
describe "Screenreader Gradebook" do
include_context "in-process server selenium tests"
include_context "reusable_gradebook_course"
include GradebookCommon
let(:default_gradebook) { "/courses/#{@course.id}/gradebook/change_gradebook_version?version=2" }
let(:button_type_submit) { f(".button_type_submit") }
let(:arrange_assignments) { f("#arrange_assignments") }
let(:assign1_default_points) { 1 }
let(:assignment_default_points) { 20 }
let(:grading_value) { f(".grading_value") }
let(:gradebook_cell_css) { ".gradebook-cell" }
let(:view_grading_history) { f("a[href='/courses/#{@course.id}/gradebook/history']") }
def active_element
driver.switch_to.active_element
end
def basic_percent_setup(num = 1)
init_course_with_students num
user_session(@teacher)
@course.assignments.create!(
title: "Test 1",
submission_types: "online_text_entry",
points_possible: 20,
grading_type: "percent"
)
end
def basic_point_setup(num = 1)
init_course_with_students num
user_session(@teacher)
@curve_assignment = @course.assignments.create!(
title: "Test 1",
submission_types: "online_text_entry",
points_possible: 20,
grading_type: "points"
)
end
def simple_setup(student_number = 2)
init_course_with_students student_number
user_session(@teacher)
@course.assignment_groups.create! name: "Group 1"
@course.assignment_groups.create! name: "Group 2"
@assign1 = @course.assignments.create!(
title: "Test 1",
points_possible: assignment_default_points,
assignment_group: @course.assignment_groups[0]
)
@assign2 = @course.assignments.create!(
title: "Test 2",
points_possible: assignment_default_points,
assignment_group: @course.assignment_groups[1]
)
@grade_array = %w[15 12 11 3]
end
def simple_grade
@assign1.grade_student(@students[0], grade: @grade_array[0], grader: @teacher)
@assign1.grade_student(@students[1], grade: @grade_array[1], grader: @teacher)
@assign2.grade_student(@students[0], grade: @grade_array[2], grader: @teacher)
@assign2.grade_student(@students[1], grade: @grade_array[3], grader: @teacher)
end
it "can select a student", priority: "1" do
simple_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
simple_grade
SRGB.visit(@course.id)
student_dropdown_options = ["No Student Selected", @students[0].sortable_name, @students[1].sortable_name]
expect(get_options("#student_select").map(&:text)).to eq student_dropdown_options
click_option "#student_select", @students[0].sortable_name
assignment_points = ["(#{@grade_array[0]} / 20)", "(#{@grade_array[2]} / 20)"]
expect(ff("#student_information .assignment-subtotal-grade .points").map(&:text)).to eq assignment_points
click_option "#student_select", @students[1].sortable_name
assignment_points = ["(#{@grade_array[1]} / 20)", "(#{@grade_array[3]} / 20)"]
expect(ff("#student_information .assignment-subtotal-grade .points").map(&:text)).to eq assignment_points
end
it "can select a student using buttons", priority: "1" do
init_course_with_students 3
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
user_session(@teacher)
SRGB.visit(@course.id)
# first student
expect(SRGB.previous_student.attribute("disabled")).to be_truthy
SRGB.next_student.click
expect(f("#student_information .student_selection")).to include_text @students[0].name
# second student
SRGB.next_student.click
expect(f("#student_information .student_selection")).to include_text @students[1].name
# third student
SRGB.next_student.click
expect(SRGB.next_student.attribute("disabled")).to be_truthy
expect(f("#student_information .student_selection")).to include_text @students[2].name
expect(SRGB.previous_student).to eq driver.switch_to.active_element
# click twice to go back to first student
SRGB.previous_student.click
SRGB.previous_student.click
expect(f("#student_information .student_selection")).to include_text @students[0].name
expect(SRGB.next_student).to eq driver.switch_to.active_element
end
it "can select an assignment using buttons", priority: "2" do
simple_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
SRGB.select_student(@students[0])
SRGB.select_assignment(@assign1)
expect(SRGB.previous_assignment.attribute("disabled")).to be_truthy
expect(SRGB.next_assignment.attribute("disabled")).not_to be_truthy
SRGB.next_assignment.click
expect(SRGB.previous_assignment.attribute("disabled")).not_to be_truthy
expect(SRGB.next_assignment.attribute("disabled")).to be_truthy
SRGB.previous_assignment.click
expect(SRGB.previous_assignment.attribute("disabled")).to be_truthy
end
it "links to assignment show page", priority: "2" do
simple_setup
simple_grade
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
@submission = @assign1.submit_homework(@students[0], body: "student submission")
SRGB.visit(@course.id)
SRGB.select_student(@students[0])
SRGB.select_assignment(@assign1)
SRGB.assignment_link.click
expect(driver.current_url).to include("/courses/#{@course.id}/assignments/#{@assign1.id}")
end
it "sets default grade", priority: "2" do
skip_if_safari(:alert)
num_of_students = 2
simple_setup(num_of_students)
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
SRGB.select_student(@students[0])
SRGB.select_assignment(@assign1)
SRGB.default_grade.click
replace_content(grading_value, assign1_default_points)
button_type_submit.click
accept_alert
get default_gradebook
expect(Gradebook::Cells.get_grade(@students[0], @assign1)).to eq("1")
expect(Gradebook::Cells.get_grade(@students[1], @assign1)).to eq("1")
end
it "can select an assignment", priority: "1" do
a1 = basic_percent_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
a2 = @course.assignments.create!(
title: "Test 2",
points_possible: 20
)
a1.grade_student(@students[0], grade: 14, grader: @teacher)
SRGB.visit(@course.id)
expect(get_options("#assignment_select").map(&:text)).to eq ["No Assignment Selected", a1.name, a2.name]
click_option "#assignment_select", a1.name
expect(f("#assignment_information .assignment_selection")).to include_text a1.name
expect(f("#assignment_information")).to include_text "Online text entry"
end
it "displays/removes warning message for resubmitted assignments", priority: "1" do
skip "Skipped because this spec fails if not run in foreground\n" \
"This is believed to be the issue: https://code.google.com/p/selenium/issues/detail?id=7346"
assignment = basic_percent_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
user_session @students[0]
assignment.submit_homework @students[0], submission_type: "online_text_entry", body: "Hello!"
user_session @teacher
assignment.grade_student(@students[0], grade: 12, grader: @teacher)
user_session @students[0]
assignment.submit_homework @students[0], submission_type: "online_text_entry", body: "Hello again!"
user_session @teacher
SRGB.visit(@course.id)
click_option "#assignment_select", assignment.name
click_option "#student_select", @students[0].sortable_name
expect(f("p.resubmitted")).to be_displayed
replace_content f("#student_and_assignment_grade"), "15\t"
expect(f("#content")).not_to contain_css("p.resubmitted")
end
it "grades match default gradebook grades", priority: "1" do
skip "Skipped because this spec fails if not run in foreground\n" \
"This is believed to be the issue: https://code.google.com/p/selenium/issues/detail?id=7346"
a1 = basic_percent_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
a2 = @course.assignments.create!(
title: "Test 2",
points_possible: 20
)
grades = [15, 12]
get "/courses/#{@course.id}/gradebook"
f(".canvas_1 .slick-row .slick-cell").click
f(".canvas_1 .slick-row .slick-cell .grade").send_keys grades[0], :return
SRGB.visit(@course.id)
click_option "#student_select", @students[0].sortable_name
click_option "#assignment_select", a1.name
expect(f("#student_and_assignment_grade")).to have_value grades[0]
expect(f("#student_information .total-grade")).to include_text "75% (#{grades[0]} / 20 points)"
click_option "#assignment_select", a2.name
f("#student_and_assignment_grade").clear
f("#student_and_assignment_grade").send_keys grades[1], :return
get default_gradebook
expect(f(".canvas_1 .slick-row .slick-cell:nth-of-type(2)")).to include_text grades[1]
end
it "can mute assignments", priority: "1" do
assignment = basic_percent_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
assignment.unmute!
SRGB.visit(@course.id)
click_option "#student_select", @students[0].sortable_name
click_option "#assignment_select", assignment.name
f("#assignment_muted_check").click
fj('.ui-dialog:visible [data-action="mute"]').click
wait_for_ajax_requests
get "/courses/#{@course.id}/grades/#{@students[0].id}"
expect(f(".student_assignment.editable")).to have_attribute("data-muted", "true")
end
it "can unmute assignments", priority: "1" do
assignment = basic_percent_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
assignment.mute!
SRGB.visit(@course.id)
click_option "#student_select", @students[0].sortable_name
click_option "#assignment_select", assignment.name
f("#assignment_muted_check").click
fj('.ui-dialog:visible [data-action="unmute"]').click
wait_for_ajax_requests
get "/courses/#{@course.id}/grades/#{@students[0].id}"
expect(f(".student_assignment.editable")).to have_attribute("data-muted", "false")
end
it "can message students who...", priority: "1" do
basic_percent_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
click_option "#assignment_select", "Test 1"
f("#message_students").click
expect(f("#message_students_dialog")).to be_displayed
f("#body").send_keys("Hello!")
driver.action.send_keys(:tab).perform
driver.action.send_keys(:enter).perform
expect(f("#message_students_dialog")).not_to be_displayed
end
it "has total graded submission", priority: "1" do
assignment = basic_percent_setup 2
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
assignment.grade_student(@students[0], grade: 15, grader: @teacher)
assignment.grade_student(@students[1], grade: 5, grader: @teacher)
SRGB.visit(@course.id)
click_option "#student_select", @students[0].sortable_name
click_option "#assignment_select", assignment.name
expect(f("#assignment_information p:nth-of-type(2)")).to include_text "Graded submissions: 2"
expect(ff("#assignment_information table td").map(&:text)).to eq %w[20 10 15 5]
end
context "as a teacher" do
before(:once) do
gradebook_data_setup
end
before do
user_session(@teacher)
end
it "shows sections in drop-down", priority: "1" do
sections = []
2.times do |i|
sections << @course.course_sections.create!(name: "other section #{i}")
end
SRGB.visit(@course.id)
ui_options = Selenium::WebDriver::Support::Select.new(f("#section_select")).options.map(&:text)
sections.each do |section|
expect(ui_options.include?(section[:name])).to be_truthy
end
end
it "shows history", priority: "2" do
SRGB.visit(@course.id)
view_grading_history.click
expect(driver.page_source).to include("Gradebook History")
expect(driver.current_url).to include("gradebook/history")
end
it "shows all drop down options", priority: "2" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
arrange_assignments.click
expect(arrange_assignments).to include_text("By Assignment Group and Position\nAlphabetically\nBy Due Date")
end
it "keeps the assignment arrangement choice between reloads" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
%w[assignment_group alpha due_date].each do |assignment_order|
SRGB.sort_assignments_by(assignment_order)
refresh_page
expect(SRGB.assignment_sort_order).to eq(assignment_order)
end
end
it "focuses on accessible elements when setting default grades", priority: "1" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
skip_if_safari(:alert)
SRGB.visit(@course.id)
SRGB.select_assignment(@second_assignment)
# When the modal opens the close button should have focus
SRGB.default_grade.click
focused_classes = active_element[:class].split
expect(focused_classes).to include("ui-dialog-titlebar-close")
# When the modal closes by setting a grade
# the "set default grade" button should have focus
button_type_submit.click
accept_alert
check_element_has_focus(SRGB.default_grade)
# When the modal closes by the close button
# the "set default grade" button should have focus
driver.action.send_keys(:enter).perform # to open the modal
driver.action.send_keys(:enter).perform # to close the modal
check_element_has_focus(SRGB.default_grade)
end
describe "Download Submissions Button" do
let!(:change_first_assignment_to_media_recording) do
@first_assignment.submission_types = "media_recording"
@first_assignment.save
end
let!(:change_third_assignment_to_include_media_and_have_submission) do
@third_assignment.submission_types = "online_text_entry,media_recording"
@third_assignment.save
submission = @third_assignment.submit_homework(@student_1, body: "Can you click?")
submission.save!
end
# The Download Submission button should be displayed for online_upload,
# online_text_entry, online_url, and online_quiz assignments. It should
# not be displayed for any other types.
it "is displayed for online assignments" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
click_option "#assignment_select", "second assignment"
expect(f("#submissions_download_button")).to be_present
end
it "is not displayed for assignments which are not submitted online" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
click_option "#assignment_select", @assignment.name
expect(f("#content")).not_to contain_css("#submissions_download_button")
end
it "is displayed for assignments which allow both online and non-online submittion" do
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
SRGB.visit(@course.id)
click_option "#assignment_select", "assignment three"
expect(f("#submissions_download_button")).to be_present
end
end
end
context "curving grades" do
it "curves grades", priority: "1" do
skip_if_safari(:alert)
basic_point_setup 3
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
grades = [12, 10, 11]
3.times { |num| @curve_assignment.grade_student(@students[num], grade: grades[num], grader: @teacher) }
SRGB.visit(@course.id)
SRGB.select_assignment(@curve_assignment)
SRGB.curve_grade_button.click
assignment_name = @curve_assignment.name
# verify that the modal pops up
curve_form = GradingCurvePage.new
expect(curve_form.grading_curve_dialog_title.text).to eq "Curve Grade for #{assignment_name}"
curve_value = "10"
curve_form.edit_grade_curve(curve_value)
curve_form.curve_grade_submit
accept_alert
assignment_score = SRGB.assignment_scores.text.split
# assignment avg score, high score, low score
scores_as_string = %w[13 20 8]
3.times { |num| expect(assignment_score[num + 1]).to eq(scores_as_string[num]) }
end
end
end

View File

@ -1,86 +0,0 @@
# frozen_string_literal: true
#
# Copyright (C) 2016 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require_relative "../../helpers/gradebook_common"
require_relative "../pages/srgb_page"
require_relative "../pages/gradebook_cells_page"
describe "Screenreader Gradebook Student Information" do
include_context "in-process server selenium tests"
include_context "reusable_gradebook_course"
include GradebookCommon
let(:srgb_page) { SRGB }
let(:course_setup) do
enroll_teacher_and_students
assignment_1
assignment_5
student_submission
assignment_1.grade_student(student, grade: 3, grader: teacher)
end
context "in Student Information section" do
before do
course_setup
@course.root_account.disable_feature!(:individual_gradebook_enhancements)
user_session(teacher)
srgb_page.visit(test_course.id)
end
it "allows comments in Notes field", priority: "2" do
skip_if_chrome("fails in chrome - due to replace content")
srgb_page.select_student(student)
srgb_page.show_notes_option.click
replace_content(srgb_page.notes_field, "Good job!")
srgb_page.tab_out_of_input(srgb_page.notes_field)
expect(srgb_page.notes_field).to have_value("Good job!")
end
it "displays student's grades", priority: "2" do
srgb_page.select_student(student)
expect(srgb_page.final_grade.text).to eq("30% (3 / 10 points)")
expect(srgb_page.assign_subtotal_grade.text).to eq("30% (3 / 10)")
expect_new_page_load { srgb_page.switch_to_default_gradebook }
expect(Gradebook::Cells.get_total_grade(student)).to eq("30%")
end
context "displays no points possible warning" do
before do
@course.apply_assignment_group_weights = true
@course.save!
srgb_page.visit(test_course.id)
end
it "with only a student selected", priority: "2" do
srgb_page.select_student(student)
expect(f("span.text-error > i.icon-warning")).to be_displayed
expect(f("#student_information > div.row-fluid")).to include_text("Score does not include assignments from the group")
end
it "with only an assignment is selected", priority: "2" do
srgb_page.select_assignment(assignment_5)
expect(f("a > i.icon-warning")).to be_displayed
expect(f("#assignment_information > div.row-fluid")).to include_text("Assignments in this group have no points")
end
end
end
end

View File

@ -1,7 +1,6 @@
{
"imports": {
"#params": "./params.js",
"#webpack-ember-handlebars-loader": "./webpack/emberHandlebars.js",
"#webpack-i18nliner-handlebars-loader": "./webpack/i18nLinerHandlebars.js"
}
}

View File

@ -88,11 +88,6 @@ module.exports = {
},
],
},
{
test: /\.hbs$/,
include: [path.join(canvasDir, 'ui/features/screenreader_gradebook/jst')],
use: [require.resolve('#webpack-ember-handlebars-loader')],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],

View File

@ -1,96 +0,0 @@
/* eslint-disable import/no-unresolved */
/*
* Copyright (C) 2015 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// This is basically one big port of what we do in ruby-land in the
// handlebars-tasks gem. We need to run handlebars source through basic
// compilation to extract i18nliner scopes, and then we wrap the resulting
// template in an AMD module, giving it dependencies on handlebars, it's scoped
// i18n object if it needs one, and any brandableCss variant stuff it needs.
const path = require('path')
const Handlebars = require('handlebars')
const EmberHandlebars = require('ember-template-compiler').EmberHandlebars
const {readI18nScopeFromJSONFile} = require('@instructure/i18nliner-canvas/scoped_hbs_resolver')
const ScopedHbsExtractor = require('@instructure/i18nliner-canvas/scoped_hbs_extractor')
const ScopedHbsPreProcessor = require('@instructure/i18nliner-canvas/scoped_hbs_pre_processor')
const {canvasDir} = require('../params')
function compileHandlebars(data) {
const {path, source} = data
try {
let translationCount = 0
const ast = Handlebars.parse(source)
const scope = readI18nScopeFromJSONFile(path)
const extractor = new ScopedHbsExtractor(ast, {path, scope})
ScopedHbsPreProcessor.processWithScope(scope, ast)
extractor.forEach(() => translationCount++)
const precompiler = data.ember ? EmberHandlebars : Handlebars
const template = precompiler.precompile(ast).toString()
const payload = {template, scope, translationCount}
return payload
} catch (e) {
// eslint-disable-next-line no-ex-assign
e = e.message || e
// eslint-disable-next-line no-console
console.log(e)
// eslint-disable-next-line no-throw-literal
throw {error: e}
}
}
function emitTemplate({name, template, dependencies}) {
return `
import Ember from 'ember';
${dependencies.map(d => `import ${JSON.stringify(d)};`).join('\n')}
const template = Ember.Handlebars.template(${template});
Ember.TEMPLATES['${name}'] = template;
export default template;
`
}
const withLeadingDotSlash = x => (x.startsWith('.') ? x : `./${x}`)
const emberHelpers = path.resolve(
canvasDir,
'ui/features/screenreader_gradebook/ember/helpers/common.js'
)
const emberJSTRoot = path.resolve(canvasDir, 'ui/features/screenreader_gradebook/jst')
module.exports = function (source) {
this.cacheable()
const pathFromMeToEmberHelpers = withLeadingDotSlash(path.relative(this.context, emberHelpers))
const name = this.resourcePath.slice(emberJSTRoot.length + 1).replace(/\.hbs$/, '')
const dependencies = [pathFromMeToEmberHelpers]
const result = compileHandlebars({path: this.resourcePath, source, ember: true})
if (result.error) {
console.log('THERE WAS AN ERROR IN PRECOMPILATION', result)
throw result
}
if (result.translationCount > 0) {
dependencies.push('@canvas/i18n')
}
return emitTemplate({name, template: result.template, dependencies})
}

View File

@ -23,7 +23,6 @@
// template in an AMD module, giving it dependencies on handlebars, it's scoped
// i18n object if it needs one.
const Handlebars = require('handlebars')
const {EmberHandlebars} = require('ember-template-compiler')
const ScopedHbsExtractor = require('@instructure/i18nliner-canvas/scoped_hbs_extractor')
const ScopedHbsPreProcessor = require('@instructure/i18nliner-canvas/scoped_hbs_pre_processor')
const {readI18nScopeFromJSONFile} = require('@instructure/i18nliner-canvas/scoped_hbs_resolver')
@ -44,7 +43,7 @@ const compileHandlebars = data => {
ScopedHbsPreProcessor.processWithScope(scope, ast)
extractor.forEach(() => translationCount++)
const precompiler = data.ember ? EmberHandlebars : Handlebars
const precompiler = Handlebars
const template = precompiler.precompile(ast).toString()
return {template, scope, translationCount}
} catch (e) {

View File

@ -33,7 +33,6 @@ const isDev = process.env.NODE_ENV === 'development'
const {
swc,
css,
emberHandlebars,
fonts,
handlebars,
images,
@ -190,7 +189,6 @@ module.exports = {
fonts,
...swc,
handlebars,
emberHandlebars,
].filter(Boolean),
},

View File

@ -171,12 +171,6 @@ exports.handlebars = {
],
}
exports.emberHandlebars = {
test: /\.hbs$/,
include: [join(canvasDir, 'ui/features/screenreader_gradebook/jst')],
use: [require.resolve('./emberHandlebars')],
}
// since istanbul-instrumenter-loader adds so much overhead,
// only use it when generating crystalball map
// i.e. process.env.CRYSTALBALL_MAP === '1'

View File

@ -187,7 +187,6 @@ const featureBundles: {
rubric_assessment: () => import('./features/rubric_assessment/index'),
rubrics_index: () => import('./features/rubrics_index/index'),
rubrics_show: () => import('./features/rubrics_show/index'),
screenreader_gradebook: () => import('./features/screenreader_gradebook/index'),
search: () => import('./features/search/index'),
section: () => import('./features/section/index'),
select_content_dialog: () => import('./features/select_content_dialog/index'),

View File

@ -57,9 +57,6 @@ export default function EnhancedIndividualGradebookWrapper() {
<GradebookMenu
courseUrl={ENV.GRADEBOOK_OPTIONS.context_url}
learningMasteryEnabled={Boolean(ENV.GRADEBOOK_OPTIONS.outcome_gradebook_enabled)}
enhancedIndividualGradebookEnabled={Boolean(
ENV.GRADEBOOK_OPTIONS.individual_gradebook_enhancements
)}
variant="EnhancedIndividualGradebook"
/>
<ApolloProvider client={client}>

View File

@ -5192,7 +5192,6 @@ class Gradebook extends React.Component<GradebookProps, GradebookState> {
<Portal node={this.props.gradebookMenuNode}>
<GradebookMenu
courseUrl={this.options.context_url}
enhancedIndividualGradebookEnabled={this.options.individual_gradebook_enhancements}
learningMasteryEnabled={this.options.outcome_gradebook_enabled}
variant="DefaultGradebook"
/>

View File

@ -144,7 +144,6 @@ export type GradebookOptions = {
grading_standard_points_based: boolean
group_weighting_scheme: null | string
has_modules: boolean
individual_gradebook_enhancements: boolean
late_policy: LatePolicy | null
login_handle_name: null | string
message_attachment_upload_folder_id: string

View File

@ -26,7 +26,6 @@ ready(() => {
<GradebookHistoryApp
courseUrl={ENV.COURSE_URL}
learningMasteryEnabled={ENV.OUTCOME_GRADEBOOK_ENABLED}
enhancedIndividualGradebookEnabled={ENV.individual_gradebook_enhancements}
/>,
document.getElementById('content')
)

View File

@ -31,14 +31,9 @@ const I18n = useI18nScope('gradebook_history')
type Props = {
courseUrl: string
learningMasteryEnabled?: boolean
enhancedIndividualGradebookEnabled?: boolean
}
const GradebookHistoryApp = ({
courseUrl,
learningMasteryEnabled,
enhancedIndividualGradebookEnabled,
}: Props) => (
const GradebookHistoryApp = ({courseUrl, learningMasteryEnabled}: Props) => (
<Provider store={GradebookHistoryStore}>
<div>
<h1 className="screenreader-only">{I18n.t('Gradebook History')}</h1>
@ -51,7 +46,6 @@ const GradebookHistoryApp = ({
>
<GradebookMenu
courseUrl={courseUrl}
enhancedIndividualGradebookEnabled={enhancedIndividualGradebookEnabled}
learningMasteryEnabled={learningMasteryEnabled}
variant="GradebookHistory"
/>

View File

@ -109,9 +109,6 @@ export default class LearningMastery {
const props = {
courseUrl: this.options.context_url,
learningMasteryEnabled: true,
enhancedIndividualGradebookEnabled: Boolean(
ENV.GRADEBOOK_OPTIONS.individual_gradebook_enhancements
),
variant: 'DefaultGradebookLearningMastery',
}
ReactDOM.render(<GradebookMenu {...props} />, $container)

View File

@ -1 +0,0 @@
ember/helpers/t.js

View File

@ -1,69 +0,0 @@
/*
* Copyright (C) 2016 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import 'jquery-migrate'
import GradebookHelpers from '../helpers'
import GradebookConstants from '../constants'
QUnit.module('GradebookHelpers#noErrorsOnPage', {
setup() {
sandbox.stub($, 'find')
},
})
test('noErrorsOnPage returns true when the dom has no errors', () => {
$.find.returns([])
ok(GradebookHelpers.noErrorsOnPage())
})
test('noErrorsOnPage returns false when the dom contains errors', () => {
$.find.returns(['dom element with error message'])
notOk(GradebookHelpers.noErrorsOnPage())
})
QUnit.module('GradebookHelpers#textareaIsGreaterThanMaxLength')
test('textareaIsGreaterThanMaxLength is false at exactly the max allowed length', () =>
notOk(GradebookHelpers.textareaIsGreaterThanMaxLength(GradebookConstants.MAX_NOTE_LENGTH)))
test('textareaIsGreaterThanMaxLength is true at greater than the max allowed length', () =>
ok(GradebookHelpers.textareaIsGreaterThanMaxLength(GradebookConstants.MAX_NOTE_LENGTH + 1)))
QUnit.module('GradebookHelpers#maxLengthErrorShouldBeShown', {
setup() {
sandbox.stub($, 'find')
},
})
test('maxLengthErrorShouldBeShown is false when text length is exactly the max allowed length', () =>
notOk(GradebookHelpers.maxLengthErrorShouldBeShown(GradebookConstants.MAX_NOTE_LENGTH)))
test('maxLengthErrorShouldBeShown is false when there are DOM errors', () => {
$.find.returns(['dom element with error message'])
notOk(GradebookHelpers.maxLengthErrorShouldBeShown(GradebookConstants.MAX_NOTE_LENGTH + 1))
})
test(
'maxLengthErrorShouldBeShown is true when text length is greater than' +
'the max allowed length AND there are no DOM errors',
() => {
$.find.returns([])
ok(GradebookHelpers.maxLengthErrorShouldBeShown(GradebookConstants.MAX_NOTE_LENGTH + 1))
}
)

View File

@ -1,23 +0,0 @@
/*
* Copyright (C) 2016 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const constants = {
MAX_NOTE_LENGTH: 255,
}
export default constants

View File

@ -1,79 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {useScope as useI18nScope} from '@canvas/i18n'
import Ember from 'ember'
import AssignmentMuter from '../../jquery/AssignmentMuter'
const I18n = useI18nScope('sr_gradebook')
// http://emberjs.com/guides/components/
// http://emberjs.com/api/classes/Ember.Component.html
const AssignmentMuterComponent = Ember.Component.extend({
click(e) {
e.preventDefault()
if (this.get('assignment.muted')) {
this.unmute()
} else {
this.mute()
}
},
mute() {
return AssignmentMuter.prototype.showDialog.call(this.muter)
},
unmute() {
return AssignmentMuter.prototype.confirmUnmute.call(this.muter)
},
tagName: 'input',
type: 'checkbox',
attributeBindings: ['type', 'checked', 'ariaLabel:aria-label', 'disabled'],
checked: function () {
return this.get('assignment.muted')
}.property('assignment.muted'),
ariaLabel: function () {
if (this.get('assignment.muted')) {
return I18n.t('assignment_muted', 'Click to unmute.')
} else {
return I18n.t('assignment_unmuted', 'Click to mute.')
}
}.property('assignment.muted'),
disabled: function () {
return (
this.get('assignment.muted') &&
this.get('assignment.moderated_grading') &&
!this.get('assignment.grades_published')
)
}.property('assignment.muted', 'assignment.moderated_grading', 'assignment.grades_published'),
setup: function () {
let assignment
if ((assignment = this.get('assignment'))) {
const url = `${ENV.GRADEBOOK_OPTIONS.context_url}/assignments/${assignment.id}/mute`
this.muter = new AssignmentMuter(null, assignment, url, Ember.set)
this.muter.show()
}
}
.observes('assignment')
.on('init'),
})
export default AssignmentMuterComponent

View File

@ -1,91 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import I18n from '@canvas/i18n'
import Ember from 'ember'
import round from '@canvas/round'
import {scoreToGrade} from '@instructure/grading-utils'
import {scoreToPercentage, scoreToScaledPoints} from '@canvas/grading/GradeCalculationHelper'
const AssignmentSubtotalGradesComponent = Ember.Component.extend({
tagName: '',
subtotal: null,
student: null,
weightingScheme: null,
gradingStandard: null,
hasGrade: Ember.computed.bool('values.possible'),
hasWeightedGroups: Ember.computed.equal('weightingScheme', 'percent'),
letterGrade: function () {
const standard = this.get('gradingStandard')
if (!standard || !this.get('hasGrade')) {
return null
}
const percentage = parseFloat(this.get('rawPercent').toPrecision(4))
return scoreToGrade(percentage, standard)
}.property('gradingStandard', 'hasGrade'),
values: function () {
const student = this.get('student')
return Ember.get(student, `${this.get('subtotal.key')}`)
}.property('subtotal', 'student', 'student.total_grade'),
points: function () {
const values = this.get('values')
return `${I18n.n(round(values.score, round.DEFAULT))} / ${I18n.n(
round(values.possible, round.DEFAULT)
)}`
}.property('values'),
rawPercent: function () {
const values = this.get('values')
return scoreToPercentage(values.score, values.possible)
}.property('values'),
percent: function () {
let scoreText = I18n.n(round(this.get('rawPercent'), round.DEFAULT), {percentage: true})
if (this.get('gradingStandard') && this.get('gradingStandardPointsBased')) {
const scalingFactor = this.get('gradingStandardScalingFactor')
const values = this.get('values')
if (values.possible) {
const scaledPossible = I18n.n(scalingFactor, {
precision: 1,
})
const scaledScore = I18n.n(
scoreToScaledPoints(values.score, values.possible, scalingFactor),
{
precision: 1,
}
)
scoreText = `${scaledScore} / ${scaledPossible}`
}
}
return scoreText
}.property('values'),
scoreDetail: function () {
const points = this.get('points')
return `(${points})`
}.property('points'),
weight: function () {
return I18n.n(this.get('subtotal').weight, {percentage: true})
}.property('subtotal'),
})
export default AssignmentSubtotalGradesComponent

View File

@ -1,102 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import GradebookHelpers from '../../helpers'
import GradebookConstants from '../../constants'
const CustomColumnCellComponent = Ember.Component.extend({
column: null,
student: null,
dataForStudent: null,
id: function () {
return `custom_col_${this.get('column.id')}`
}.property('column'),
dataForColumn: function () {
const studentData = this.get('dataForStudent')
if (!studentData) {
return null
}
return studentData.findBy('column_id', this.get('column.id'))
}.property('student', 'column', 'column.isLoaded', 'dataForStudent.@each.content'),
contentDidChange: function () {
return this.set('value', this.get('dataForColumn.content'))
}
.observes('dataForColumn.content')
.on('didInsertElement'),
ajax(url, options) {
const {type, data} = options
return Ember.$.ajaxJSON(url, type, data)
},
customColURL() {
return ENV.GRADEBOOK_OPTIONS.custom_column_datum_url
},
disabled: function () {
return this.get('column.isLoading') || this.get('column.read_only')
}.property('column', 'column.isLoading', 'column.read_only'),
saveURL: function () {
return this.customColURL()
.replace(/:id/, this.get('column.id'))
.replace(/:user_id/, this.get('student.id'))
}.property('column', 'student'),
focusOut() {
const value = this.$('textarea').val()
if (
(value === '' && !this.get('dataForColumn')) ||
value === this.get('dataForColumn.content')
) {
return
}
this.get('dataForColumn.content')
const xhr = this.ajax(this.get('saveURL'), {
type: 'PUT',
data: {
'column_data[content]': value,
},
})
return xhr.then(this.boundSaveSuccess)
},
textAreaInput: function (event) {
const note = event.target.value
if (GradebookHelpers.textareaIsGreaterThanMaxLength(note.length)) {
event.target.value = note.substring(0, GradebookConstants.MAX_NOTE_LENGTH)
const showError = GradebookHelpers.maxLengthErrorShouldBeShown(note.length)
if (showError) {
return GradebookHelpers.flashMaxLengthError()
}
}
}.on('input'),
bindSave: function () {
return (this.boundSaveSuccess = this.onSaveSuccess.bind(this))
}.on('init'),
onSaveSuccess(columnDatum) {
return this.sendAction('on-column-save', columnDatum, this.get('column.id'))
},
})
export default CustomColumnCellComponent

View File

@ -1,170 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
// converted to coffeescript from:
// https://gist.github.com/kselden/7758990
import {Component, get, set} from 'ember'
const doc = document
const FastSelectComponent = Component.extend({
initialized: false,
items: null,
valuePath: 'value',
labelPath: 'label',
labelDefault: null,
valueDefault: '',
value: null,
selected: null,
ariaDescribedBy: null,
tagName: 'select',
didInsertElement() {
const self = this
if (this.get('ariaDescribedBy')) {
this.$().attr('aria-describedby', this.get('ariaDescribedBy'))
}
return this.$().on('change', function () {
return set(self, 'value', this.value)
})
},
valueDidChange: function () {
let selected = null
if (this.value && this.items) {
selected = this.items.findBy(this.valuePath, this.value)
}
set(this, 'selected', selected || null)
}.observes('value'),
initialize: function () {
const value = this.value || this.valueDefault
let selected
if (value && this.items) {
selected = this.items.findBy(this.valuePath, value)
}
set(this, 'selected', selected || null)
}.on('init'),
itemsWillChange: function () {
const {items} = this
if (items) {
items.removeArrayObserver(this)
this.arrayWillChange(items, 0, get(items, 'length'), 0)
}
}
.observesBefore('items')
.on('willDestroyElement'),
itemsDidChange: function () {
const {items} = this
if (items) {
items.addArrayObserver(this)
this.arrayDidChange(items, 0, 0, get(items, 'length'))
}
}
.observes('items')
.on('didInsertElement'),
arrayWillChange(items, start, removeCount, _addCount) {
const select = get(this, 'element')
const options = select.childNodes
let i = start + removeCount - 1
if (get(this, 'hasDefaultOption')) {
start += 1
i += 1
}
return (() => {
const result = []
while (i >= start) {
select.removeChild(options[i])
result.push(i--)
}
return result
})()
},
updateSelection: function () {
const selected = get(this, 'selected')
if (!selected) {
return
}
const currentValue = get(selected, this.valuePath)
const select = this.$(`[value=${currentValue}]`)
__guard__(select != null ? select[0] : undefined, x => (x.selected = true))
if (currentValue && currentValue !== this.value) {
set(this, 'value', currentValue)
}
}.observes('selected'),
updateOptions: function () {
this.arrayWillChange(this.items, 0, get(this.items, 'length'), 0)
this.arrayDidChange(this.items, 0, 0, get(this.items, 'length'))
}.observes('labelPath'),
arrayDidChange(items, start, _removeCount, addCount) {
let value
const select = get(this, 'element')
const hasDefault = get(this, 'hasDefaultOption')
if (hasDefault) {
start += 1
}
let i = start
const l = start + addCount
while (i < l) {
const ind = hasDefault ? i - 1 : i
const item = items.objectAt(ind)
value = get(item, this.valuePath)
const label = get(item, this.labelPath)
const option = doc.createElement('option')
option.textContent = label
option.value = value
if ((this.value || this.valueDefault) === value) {
option.selected = true
set(this, 'selected', item)
}
select.appendChild(option)
i++
}
set(this, 'value', select.value)
},
insertDefaultOption: function () {
if (!this.labelDefault || !!this.hasDefaultOption) {
return
}
const select = get(this, 'element')
const option = doc.createElement('option')
option.textContent = this.labelDefault
option.value = this.valueDefault
select.appendChild(option)
set(this, 'hasDefaultOption', true)
}
.observes('items')
.on('didInsertElement'),
})
export default FastSelectComponent
function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null ? transform(value) : undefined
}

View File

@ -1,79 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import {scoreToGrade} from '@instructure/grading-utils'
import {useScope as useI18nScope} from '@canvas/i18n'
import {scoreToScaledPoints} from '@canvas/grading/GradeCalculationHelper'
const I18n = useI18nScope('sr_gradebook')
const FinalGradeComponent = Ember.Component.extend({
percent: function () {
const percent = this.get('student.total_percent')
let scoreText = I18n.n(percent, {percentage: true})
if (this.get('gradingStandard') && this.get('gradingStandardPointsBased')) {
const scalingFactor = this.get('gradingStandardScalingFactor')
const studentTotalGrade = this.get('student.total_grade')
if (studentTotalGrade.possible) {
const scaledPossible = I18n.n(scalingFactor, {
precision: 1,
})
const scaledScore = I18n.n(
scoreToScaledPoints(studentTotalGrade.score, studentTotalGrade.possible, scalingFactor),
{
precision: 1,
}
)
scoreText = `${scaledScore} / ${scaledPossible}`
}
}
return scoreText
}.property('student.total_percent', 'student.total_grade', 'grading_standard', 'student'),
pointRatioDisplay: function () {
return I18n.t('final_point_ratio', '%{pointRatio} points', {pointRatio: this.get('pointRatio')})
}.property('pointRatio'),
pointRatio: function () {
return `${I18n.n(this.get('student.total_grade.score'))} / ${I18n.n(
this.get('student.total_grade.possible')
)}`
}.property('hide_points_possible', 'student.total_grade.score', 'student.total_grade.possible'),
letterGrade: function () {
const percent = this.get('student.total_percent')
return scoreToGrade(percent, this.get('gradingStandard'))
}.property('gradingStandard', 'percent'),
showGrade: Ember.computed.bool('student.total_grade.possible'),
showPoints: function () {
return !!(!this.get('hide_points_possible') && this.get('student.total_grade'))
}.property('hide_points_possible', 'student.total_grade'),
showLetterGrade: Ember.computed.bool('gradingStandard'),
actions: {
onEditFinalGradeOverride(grade) {
this.sendAction('onEditFinalGradeOverride', grade)
},
},
})
export default FinalGradeComponent

View File

@ -1,70 +0,0 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import Ember from 'ember'
import {scoreToGrade} from '@instructure/grading-utils'
import GradeFormatHelper from '@canvas/grading/GradeFormatHelper'
const FinalGradeOverrideComponent = Ember.Component.extend({
inputValue: null,
internalInputValue: null,
inputDescription: function () {
const percentage = this.get('finalGradeOverride.percentage')
const gradingStandard = this.get('gradingStandard')
if (percentage == null || !gradingStandard) {
return null
}
if (gradingStandard) {
if (this.get('gradingStandardPointsBased')) {
// points based grading schemes never show percentages
return null
}
}
return GradeFormatHelper.formatGrade(percentage, {gradingType: 'percent'})
}.property('finalGradeOverride', 'gradingStandard', 'gradingStandardPointsBased'),
finalGradeOverrideChanged: function () {
const percentage = this.get('finalGradeOverride.percentage')
const gradingStandard = this.get('gradingStandard')
if (percentage == null) {
this.set('internalInputValue', null)
} else if (!gradingStandard) {
this.set(
'internalInputValue',
GradeFormatHelper.formatGrade(percentage, {gradingType: 'percent'})
)
} else {
this.set('internalInputValue', scoreToGrade(percentage, gradingStandard))
}
this.set('inputValue', this.get('internalInputValue'))
}
.observes('finalGradeOverride', 'gradingStandard')
.on('init'),
focusOut() {
this.sendAction('onEditFinalGradeOverride', this.get('inputValue'))
// Always show a valid grade in the input on blur.
this.set('inputValue', this.get('internalInputValue'))
},
})
export default FinalGradeOverrideComponent

View File

@ -1,274 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {useScope as useI18nScope} from '@canvas/i18n'
import numberHelper from '@canvas/i18n/numberHelper'
import GRADEBOOK_TRANSLATIONS from '@canvas/grading/GradebookTranslations'
import GradeFormatHelper from '@canvas/grading/GradeFormatHelper'
import OutlierScoreHelper from '@canvas/grading/OutlierScoreHelper'
import Ember from 'ember'
import $ from 'jquery'
import '@canvas/jquery/jquery.ajaxJSON'
const I18n = useI18nScope('grading_cell')
const GradingCellComponent = Ember.Component.extend({
value: null,
excused: null,
shouldSaveExcused: false,
isPoints: Ember.computed.equal('assignment.grading_type', 'points'),
isPercent: Ember.computed.equal('assignment.grading_type', 'percent'),
isLetterGrade: Ember.computed.equal('assignment.grading_type', 'letter_grade'),
isPassFail: Ember.computed.equal('assignment.grading_type', 'pass_fail'),
isInPastGradingPeriodAndNotAdmin: function () {
return this.submission != null ? this.submission.gradeLocked : undefined
}.property('submission'),
nilPointsPossible: Ember.computed.none('assignment.points_possible'),
isGpaScale: Ember.computed.equal('assignment.grading_type', 'gpa_scale'),
passFailGrades: [
{
label: I18n.t('grade_ungraded', 'Ungraded'),
value: '-',
},
{
label: I18n.t('grade_complete', 'Complete'),
value: 'complete',
},
{
label: I18n.t('grade_incomplete', 'Incomplete'),
value: 'incomplete',
},
{
label: I18n.t('Excused'),
value: 'EX',
},
],
outOfText: function () {
if (this.submission && this.submission.excused) {
return I18n.t('Excused')
} else if (this.get('isGpaScale')) {
return ''
} else if (this.get('isLetterGrade') || this.get('isPassFail')) {
return I18n.t('(%{score} out of %{points})', {
points: I18n.n(this.assignment.points_possible),
score: this.get('entered_score'),
})
} else if (this.get('nilPointsPossible')) {
return I18n.t('No points possible')
} else {
return I18n.t('(out of %{points})', {points: I18n.n(this.assignment.points_possible)})
}
}.property('submission.score', 'assignment'),
changeGradeURL() {
return ENV.GRADEBOOK_OPTIONS.change_grade_url
},
isExcused(value) {
return (
value &&
typeof value === 'string' &&
(value?.toUpperCase() === 'EXCUSED' || value?.toUpperCase() === 'EX')
)
},
saveURL: function () {
const submission = this.get('submission')
return this.changeGradeURL()
.replace(':assignment', submission.assignment_id)
.replace(':submission', submission.user_id)
}.property('submission.assignment_id', 'submission.user_id'),
score: function () {
if (this.submission.score != null) {
return I18n.n(this.submission.score)
} else {
return ' -'
}
}.property('submission.score'),
entered_score: function () {
if (this.submission.entered_score != null) {
return I18n.n(this.submission.entered_score)
} else {
return ' -'
}
}.property('submission.entered_score'),
late_penalty: function () {
if (this.submission.points_deducted != null) {
return I18n.n(-1 * this.submission.points_deducted)
} else {
return ' -'
}
}.property('submission.points_deducted'),
points_possible: function () {
if (this.assignment.points_possible != null) {
return I18n.n(this.assignment.points_possible)
} else {
return ' -'
}
}.property('assignment.points_possible'),
final_grade: function () {
if (this.submission.grade != null) {
return GradeFormatHelper.formatGrade(this.submission.grade)
} else {
return ' -'
}
}.property('submission.grade'),
ajax(url, options) {
const {type, data} = options
return $.ajaxJSON(url, type, data)
},
excusedToggled: function () {
if (this.shouldSaveExcused) {
this.updateSubmissionExcused()
}
}.observes('excused'),
updateSubmissionExcused() {
const url = this.get('saveURL')
const value = __guard__(this.$('#submission-excused'), x => x[0].checked)
const save = this.ajax(url, {
type: 'PUT',
data: {'submission[excuse]': value},
})
return save.then(this.boundUpdateSuccess, this.onUpdateError)
},
setExcusedWithoutTriggeringSave(isExcused) {
this.shouldSaveExcused = false
this.set('excused', isExcused)
return (this.shouldSaveExcused = true)
},
submissionDidChange: function () {
const newVal = (this.submission != null ? this.submission.excused : undefined)
? 'EX'
: (this.submission != null ? this.submission.entered_grade : undefined) || '-'
this.setExcusedWithoutTriggeringSave(
this.submission != null ? this.submission.excused : undefined
)
if (this.get('isPassFail')) {
this.set('value', newVal)
} else {
this.set('value', GradeFormatHelper.formatGrade(newVal))
}
}
.observes('submission')
.on('init'),
onUpdateSuccess(submission) {
this.sendAction('on-submit-grade', submission.all_submissions)
if (!submission.excused) {
const outlierScoreHelper = new OutlierScoreHelper(
submission.score,
this.assignment.points_possible
)
if (outlierScoreHelper.hasWarning()) {
$.flashWarning(outlierScoreHelper.warningMessage())
}
}
},
onUpdateError() {
$.flashError(GRADEBOOK_TRANSLATIONS.submission_update_error)
},
focusOut(event) {
const isGradeInput = event.target.id === 'student_and_assignment_grade'
const submission = this.get('submission')
if (!submission || !isGradeInput) {
return
}
const url = this.get('saveURL')
let value = this.$('input, select').val() || this.value
const excused = this.isExcused(value)
if (this.get('excused') && excused) {
return
} else {
this.setExcusedWithoutTriggeringSave(excused)
}
if (this.get('isPassFail') && value === '-') {
value = ''
}
value = GradeFormatHelper.delocalizeGrade(value)
if (
value === submission.grade ||
((value === '-' || value === '') && submission.grade === null)
) {
return
}
if ((!this.isExcused(value) && this.get('isPoints')) || this.get('isPercent')) {
let formattedGrade = value
formattedGrade = numberHelper.parse(formattedGrade?.replace(/%/g, '')).toString()
if (formattedGrade === 'NaN') {
return $.flashError(I18n.t('Invalid Grade'))
}
}
const data = this.isExcused(value)
? {'submission[excuse]': true}
: {'submission[posted_grade]': value}
const save = this.ajax(url, {
type: 'PUT',
data,
})
return save.then(this.boundUpdateSuccess, this.onUpdateError)
},
bindSave: function () {
this.boundUpdateSuccess = this.onUpdateSuccess.bind(this)
}.on('init'),
click(event) {
const {target} = event
const hasCheckboxClass = target.classList[0] === 'checkbox'
const isCheckBox = target.type === 'checkbox'
if (hasCheckboxClass || isCheckBox) {
return this.$('#submission-excused').focus()
} else {
return this.$('input, select').select()
}
},
focus() {
return this.$('input, select').select()
},
})
export default GradingCellComponent
function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null ? transform(value) : undefined
}

View File

@ -1,190 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import {useScope as useI18nScope} from '@canvas/i18n'
import Ember from 'ember'
import register from '../helpers/register'
import '../../jst/components/ic-submission-download-dialog.hbs'
import 'jqueryui/progressbar'
import 'jqueryui/dialog'
const I18n = useI18nScope('submissions')
// example usage:
// {{
// ic-submission-download-dialog
// submissionsDownloadUrl=assignment_submissions_url
// }}
export default register(
'component',
'ic-submission-download-dialog',
Ember.Component.extend({
isOpened: false,
isChecking: true,
attachment: {},
percentComplete: 0,
hideIndicator: Ember.computed.not('isChecking'),
showFileLink: Ember.computed.equal('status', 'finished'),
sizeOfFile: Ember.computed.alias('attachment.readable_size'),
dialogTitle: I18n.t('download_submissions_title', 'Download Assignment Submissions'),
bindFunctions: function () {
this.reviewProgress = this.reviewProgress.bind(this)
this.progressError = this.progressError.bind(this)
return (this.checkForChange = this.checkForChange.bind(this))
}.on('init'),
status: function () {
if (this.fileReady()) {
return 'finished'
} else if (this.get('percentComplete') >= 95) {
return 'zipping'
} else {
return 'starting'
}
}.property('attachment', 'percentComplete', 'isOpened'),
progress: function () {
const attachment = this.get('attachment')
let new_val = 0
if (attachment && this.fileReady()) {
new_val = 100
} else if (attachment) {
new_val = this.get('percentComplete')
if (this.get('percentComplete') < 95) {
new_val += 5
}
const state = parseInt(this.get('attachment.file_state'), 10)
if (Number.isNaN(Number(state))) {
new_val = 0
}
}
return this.set('percentComplete', new_val)
}.observes('attachment'),
keepChecking: function () {
if (this.get('percentComplete') !== 100 && !!this.get('isOpened')) {
return true
}
}.property('percentComplete', 'isOpened'),
url: function () {
return `${this.get('submissionsDownloadUrl')}`
}.property('submissionsDownloadUrl'),
statusText: function () {
switch (this.get('status')) {
case 'starting':
return I18n.t('gathering_files', 'Gathering Files (%{progress})...', {
progress: I18n.toPercentage(this.get('percentComplete'), {precision: 0}),
})
case 'zipping':
return I18n.t('creating_zip', 'Creating zip file...')
case 'finished':
return I18n.t('finished_redirecting', 'Finished! Redirecting to File...')
}
}.property('status', 'percentComplete'),
updateProgressBar: function () {
return $('#progressbar').progressbar({value: this.get('percentComplete')})
}.observes('percentComplete'),
downloadCompletedFile: function () {
if (this.get('percentComplete') === 100) {
return (window.location.href = this.get('url'))
}
}.observes('percentComplete'),
resetAttachment: function () {
return this.set('attachment', null)
}.observes('isOpened'),
closeOnEsc: function (event) {
if (event.keyCode === 27) {
// esc
return this.close()
}
}.on('keyDown'),
actions: {
openDialog() {
this.set('isOpened', true)
if (this.dialogOptions == null) {
this.dialogOptions = {
title: 'Download Assignment Submissions',
resizable: false,
modal: true,
zIndex: 1000,
}
}
if (this.$dialog == null) {
this.$dialog = $('#submissions_download_dialog form').dialog(this.dialogOptions)
}
this.$dialog.dialog({
modal: true,
zIndex: 1000,
})
return this.checkForChange()
},
closeDialog() {
return this.close()
},
},
close() {
this.$dialog.dialog('close')
return this.set('isOpened', false)
},
fileReady() {
const state = this.get('attachment.workflow_state')
return state === 'zipped' || state === 'available'
},
checkForChange() {
this.set('isChecking', true)
return $.ajaxJSON(this.get('url'), 'GET', {}, this.reviewProgress, this.progressError)
},
reviewProgress(data) {
this.set('isChecking', false)
this.set('attachment', data.attachment)
return this.setCheckTimeOut(3000)
},
progressError() {
this.set('isChecking', false)
return this.setCheckTimeOut(1000)
},
setCheckTimeOut(time) {
if (this.get('keepChecking')) {
return setTimeout(this.checkForChange, time)
}
},
})
)

View File

@ -1,23 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import '@canvas/handlebars-helpers'
export default Ember.Application.extend({
rootElement: '#content',
})

View File

@ -1,21 +0,0 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export default function route() {
return this.resource('screenreader_gradebook', {path: '/'})
}

View File

@ -1,25 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
// This file gets included in all ember templates, ever, therefore:
// - Only require helpers that are necessary for all ember apps.
// - Be strict about what you include or allow to be included here.
// - Don't write code here, include helpers from their own files
import './t'
import './format-date'
import './n'

View File

@ -1,37 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import * as tz from '@instructure/moment-utils'
const {Handlebars} = Ember
// #
// Formats a parsable date with normal strftime formats
//
// ```html
// {{format-date datetime '%b %d'}}
// ```
export default Handlebars.registerBoundHelper('format-date', (datetime, format) => {
if (datetime == null) {
return
}
if (typeof format !== 'string') {
format = '%b %e, %Y %l:%M %P'
}
return tz.format(tz.parse(datetime), format)
})

View File

@ -1,26 +0,0 @@
//
// Copyright (C) 2017 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import I18n from '@canvas/i18n'
import numberFormat from '@canvas/i18n/numberFormat'
Ember.Handlebars.registerBoundHelper('n', (number, options) => I18n.n(number, options.hash))
export default Ember.Handlebars.registerBoundHelper('nf', (number, options) =>
numberFormat[options.hash.format](number)
)

View File

@ -1,31 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
// Registers objects on the application container, like components. This
// prevents us from having to add App.WhateverThing to every app, and instead
// just require the shared object into your app without any extra fuss.
export default function register(type, name, obj) {
Ember.Application.initializer({
name,
initialize(container) {
return container.register(`${type}:${name}`, obj)
},
})
return obj
}

View File

@ -1,58 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import I18n from '@canvas/i18n'
import htmlEscape from '@instructure/html-escape'
Ember.Handlebars.registerHelper('t', (...args1) => {
const adjustedLength = Math.max(args1.length, 1),
args = args1.slice(0, adjustedLength - 1),
hbsOptions = args1[adjustedLength - 1]
const {hash, hashTypes, hashContexts} = hbsOptions
const options = {}
for (const key of Object.keys(hash || {})) {
const value = hash[key]
const type = hashTypes[key]
if (type === 'ID') {
options[key] = Ember.get(hashContexts[key], value)
} else {
options[key] = value
}
}
const wrappers = []
let key
while ((key = `w${wrappers.length}`) && options[key]) {
wrappers.push(options[key])
delete options[key]
}
if (wrappers.length) {
options.wrapper = wrappers
}
return new Ember.Handlebars.SafeString(htmlEscape(I18n.t(...Array.from(args), options)))
})
Ember.Handlebars.registerHelper('__i18nliner_escape', htmlEscape)
Ember.Handlebars.registerHelper('__i18nliner_safe', val => new htmlEscape.SafeString(val))
export default Ember.Handlebars.registerHelper('__i18nliner_concat', (...args1) => {
const adjustedLength = Math.max(args1.length, 1),
args = args1.slice(0, adjustedLength - 1)
return args.join('')
})

View File

@ -1,44 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {$, ArrayProxy} from 'ember'
import ajax from 'ic-ajax'
import parseLinkHeader from './parse_link_header'
function fetch(url, options) {
const opts = $.extend({dataType: 'json'}, {data: options.data})
const {records} = options
return ajax.raw(url, opts).then(result => {
const response = options.process ? options.process(result.response) : result.response
records.pushObjects(response)
const meta = parseLinkHeader(result.jqXHR)
if (meta.next) {
return fetch(meta.next, options)
} else {
records.set('isLoaded', true)
records.set('isLoading', false)
return records
}
})
}
export default function fetchAllPages(url, options = {}) {
const records = options.records || (options.records = ArrayProxy.create({content: []}))
records.set('isLoading', true)
records.set('promise', fetch(url, options))
return records
}

View File

@ -1,32 +0,0 @@
/*
* Copyright (C) 2018 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const regex = /<(http.*?)>; rel="([a-z]*)",/g
export default function parseLinkHeader(jqXhr) {
let link
const links = {}
const header = jqXhr.getResponseHeader('Link')
if (!header) {
return links
}
while ((link = regex.exec(header))) {
links[link[2]] = link[1]
}
return links
}

View File

@ -1,84 +0,0 @@
//
// Copyright (C) 2017 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import App from './config/app'
import routes from './config/routes'
import AssignmentSubtotalGradesComponent from './components/assignment_subtotal_grades_component'
import AssignmentMuterComponent from './components/assignment_muter_component'
import CustomColumnCellComponent from './components/custom_column_cell_component'
import FastSelectComponent from './components/fast_select_component'
import FinalGradeComponent from './components/final_grade_component'
import FinalGradeOverrideComponent from './components/final_grade_override_component'
import GradingCellComponent from './components/grading_cell_component'
import ScreenreaderGradebookController from './controllers/screenreader_gradebook_controller'
import ScreenreaderGradebookRoute from './routes/screenreader_gradebook_route'
import AssignmentsView from './views/assignments_view'
import LearningMasteryView from './views/learning_mastery_view'
import ScreenreaderGradebookView from './views/screenreader_gradebook_view'
import SelectionButtonsView from './views/selection_buttons_view'
import '../jst/aria_announcer.hbs'
import '../jst/assignment_information/actions.hbs'
import '../jst/assignment_information/details.hbs'
import '../jst/assignment_information/index.hbs'
import '../jst/assignments.hbs'
import '../jst/components/assignment-subtotal-grades.hbs'
import '../jst/components/custom-column-cell.hbs'
import '../jst/components/final-grade.hbs'
import '../jst/components/final-grade-override.hbs'
import '../jst/components/grading-cell.hbs'
import '../jst/content_selection/assignment.hbs'
import '../jst/content_selection/header.hbs'
import '../jst/content_selection/outcome.hbs'
import '../jst/content_selection/selection_buttons.hbs'
import '../jst/content_selection/student.hbs'
import '../jst/gradebookHeader.hbs'
import '../jst/grading.hbs'
import '../jst/learning_mastery.hbs'
import '../jst/outcome_information.hbs'
import '../jst/screenreader_gradebook.hbs'
import '../jst/settings/assignment_toggles_and_actions.hbs'
import '../jst/settings/grading_period_select.hbs'
import '../jst/settings/header.hbs'
import '../jst/settings/mastery_toggles_and_actions.hbs'
import '../jst/settings/section_select.hbs'
import '../jst/settings/sort_select.hbs'
import '../jst/student_information/assignment_subtotals.hbs'
import '../jst/student_information/details.hbs'
import '../jst/student_information/index.hbs'
App.initializer({
name: 'routes',
initialize(container, application) {
return application.Router.map(routes)
},
})
export default App.reopen({
AssignmentSubtotalGradesComponent,
AssignmentMuterComponent,
CustomColumnCellComponent,
FastSelectComponent,
FinalGradeComponent,
FinalGradeOverrideComponent,
GradingCellComponent,
ScreenreaderGradebookController,
ScreenreaderGradebookRoute,
AssignmentsView,
LearningMasteryView,
ScreenreaderGradebookView,
SelectionButtonsView,
})

View File

@ -1,73 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import {ArrayProxy, ObjectProxy, Route} from 'ember'
import {flattenDeep} from 'lodash'
import fetchAllPages from '../helpers/xhr/fetch_all_pages'
import {getFinalGradeOverrides} from '@canvas/grading/FinalGradeOverrideApi'
const ScreenreaderGradebookRoute = Route.extend({
model() {
const model = {
enrollments: fetchAllPages(ENV.GRADEBOOK_OPTIONS.enrollments_url),
assignment_groups: ArrayProxy.create({content: []}),
submissions: ArrayProxy.create({content: []}),
custom_columns: fetchAllPages(ENV.GRADEBOOK_OPTIONS.custom_columns_url),
sections: fetchAllPages(ENV.GRADEBOOK_OPTIONS.sections_url),
}
if (ENV.GRADEBOOK_OPTIONS.final_grade_override_enabled) {
const records = ObjectProxy.create({content: {}, isLoaded: false})
records.set(
'promise',
getFinalGradeOverrides(ENV.GRADEBOOK_OPTIONS.context_id).then(data => {
records.set('isLoaded', true)
records.set('content', data)
})
)
model.final_grade_overrides = records
}
if (!ENV.GRADEBOOK_OPTIONS.outcome_gradebook_enabled) {
model.outcomes = model.outcome_rollups = ArrayProxy.create({content: []})
} else {
model.outcomes = fetchAllPages(ENV.GRADEBOOK_OPTIONS.outcome_links_url, {
process(response) {
return response.map(x => x.outcome)
},
})
model.outcome_rollups = fetchAllPages(ENV.GRADEBOOK_OPTIONS.outcome_rollups_url, {
process(response) {
return flattenDeep(
response.rollups.map(row =>
row.scores.map(cell => ({
user_id: row.links.user,
outcome_id: cell.links.outcome,
score: cell.score,
}))
)
)
},
})
}
return model
},
})
export default ScreenreaderGradebookRoute

View File

@ -1,39 +0,0 @@
{
"env": {
"qunit": true
},
"globals": {
"module": true,
"test": true,
"equal": true,
"ok": true,
"sandbox": true,
"sinon": true,
"deepEqual": true,
"click": true,
"andThen": true,
"visit": true,
"find": true,
"App": true,
"start": true
},
"extends": ["plugin:qunit/recommended", "plugin:qunit/two"],
"plugins": ["qunit"],
"rules": {
"no-restricted-globals": "off",
"func-names": "off",
"prefer-arrow-callback": "off",
"jest/no-identical-title": "off",
"qunit/no-global-stop-start": "off",
"qunit/no-setup-teardown": "off",
"qunit/no-global-assertions": "off",
"qunit/no-global-module-test": "off",
"qunit/require-expect": "off",
"qunit/no-assert-logical-expression": "error",
"qunit/no-commented-tests": "error",
"qunit/no-compare-relation-boolean": "error"
},
"parserOptions": {
"ecmaVersion": 2020
}
}

View File

@ -1,63 +0,0 @@
//
// Copyright (C) 2018 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
//
import Ember from 'ember'
export default class AsyncHelper {
constructor() {
this.pendingRequests = 0
}
start = () => {
Ember.RSVP.configure('instrument', true)
Ember.RSVP.on('created', this.incrementRequest)
Ember.RSVP.on('fulfilled', this.decrementRequest)
return Ember.RSVP.on('rejected', this.decrementRequest)
}
stop = () => {
Ember.RSVP.off('created', this.incrementRequest)
Ember.RSVP.off('fulfilled', this.decrementRequest)
Ember.RSVP.off('rejected', this.decrementRequest)
Ember.RSVP.configure('instrument', false)
return (this.pendingRequests = 0)
}
incrementRequest = () => {
return this.pendingRequests++
}
decrementRequest = () => {
return this.pendingRequests--
}
waitForRequests = () => {
return new Promise(resolve => {
const defer = () =>
Ember.run.later(() => {
if (this.pendingRequests) {
return defer()
} else {
return resolve()
}
})
return defer()
})
}
}

View File

@ -1,862 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import ajax from 'ic-ajax'
import Ember from 'ember'
const clone = obj => Ember.copy(obj, true)
const default_grade_response = [
{
submission: {
assignment_id: '1',
attachment_id: null,
attachment_ids: null,
attempt: null,
body: null,
cached_due_date: '2013-12-19T06:59:59Z',
context_code: 'course_2',
created_at: '2013-12-12T22:57:34Z',
grade: '100',
grade_matches_current_submission: true,
graded_at: '2013-12-16T21:25:44Z',
grader_id: '1',
group_id: null,
has_admin_comment: false,
has_rubric_assessment: null,
id: '34291',
media_comment_id: null,
media_comment_type: null,
media_object_id: null,
process_attempts: 0,
processed: null,
published_grade: '150',
published_score: 100.0,
quiz_submission_id: null,
score: 100.0,
student_entered_score: null,
submission_comments_count: null,
submission_type: null,
submitted_at: null,
turnitin_data: null,
updated_at: '2013-12-16T21:25:45Z',
url: null,
user_id: '1',
workflow_state: 'graded',
submission_history: [
{
submission: {
assignment_id: 1,
attachment_id: null,
attachment_ids: null,
attempt: null,
body: null,
cached_due_date: '2013-12-19T06:59:59Z',
context_code: 'course_2',
created_at: '2013-12-12T22:57:34Z',
grade: '100',
grade_matches_current_submission: true,
graded_at: '2013-12-16T21:25:44Z',
grader_id: 1,
group_id: null,
has_admin_comment: false,
has_rubric_assessment: null,
id: 34291,
media_comment_id: null,
media_comment_type: null,
media_object_id: null,
process_attempts: 0,
processed: null,
published_grade: '150',
published_score: 100.0,
quiz_submission_id: null,
score: 100.0,
student_entered_score: null,
submission_comments_count: null,
submission_type: null,
submitted_at: null,
turnitin_data: null,
updated_at: '2013-12-16T21:25:45Z',
url: null,
user_id: 1,
workflow_state: 'graded',
versioned_attachments: [],
},
},
],
submission_comments: [],
attachments: [],
},
},
{
submission: {
assignment_id: '1',
attachment_id: null,
attachment_ids: null,
attempt: null,
body: null,
cached_due_date: '2013-12-19T06:59:59Z',
context_code: 'course_1',
created_at: '2013-12-12T22:57:35Z',
grade: '100',
grade_matches_current_submission: true,
graded_at: '2013-12-16T21:25:47Z',
grader_id: '1',
group_id: null,
has_admin_comment: false,
has_rubric_assessment: null,
id: '34292',
media_comment_id: null,
media_comment_type: null,
media_object_id: null,
process_attempts: 0,
processed: null,
published_grade: '100',
published_score: 100.0,
quiz_submission_id: null,
score: 100.0,
student_entered_score: null,
submission_comments_count: null,
submission_type: null,
submitted_at: null,
turnitin_data: null,
updated_at: '2013-12-16T21:25:47Z',
url: null,
user_id: '2',
workflow_state: 'graded',
submission_history: [
{
submission: {
assignment_id: 1,
attachment_id: null,
attachment_ids: null,
attempt: null,
body: null,
cached_due_date: '2013-12-19T06:59:59Z',
context_code: 'course_2',
created_at: '2013-12-12T22:57:35Z',
grade: '100',
grade_matches_current_submission: true,
graded_at: '2013-12-16T21:25:47Z',
grader_id: 1,
group_id: null,
has_admin_comment: false,
has_rubric_assessment: null,
id: 34292,
media_comment_id: null,
media_comment_type: null,
media_object_id: null,
process_attempts: 0,
processed: null,
published_grade: '100',
published_score: 100.0,
quiz_submission_id: null,
score: 100.0,
student_entered_score: null,
submission_comments_count: null,
submission_type: null,
submitted_at: null,
turnitin_data: null,
updated_at: '2013-12-16T21:25:47Z',
url: null,
user_id: 2,
workflow_state: 'graded',
versioned_attachments: [],
},
},
],
submission_comments: [],
attachments: [],
},
},
{
submission: {
assignment_id: '1',
attachment_id: null,
attachment_ids: null,
attempt: null,
body: null,
cached_due_date: '2013-12-19T06:59:59Z',
context_code: 'course_2',
created_at: '2013-12-12T22:57:34Z',
grade: '100',
grade_matches_current_submission: true,
graded_at: '2013-12-16T21:25:44Z',
grader_id: '1',
group_id: null,
has_admin_comment: false,
has_rubric_assessment: null,
id: '34291',
media_comment_id: null,
media_comment_type: null,
media_object_id: null,
process_attempts: 0,
processed: null,
published_grade: '150',
published_score: 100.0,
quiz_submission_id: null,
score: 100.0,
student_entered_score: null,
submission_comments_count: null,
submission_type: null,
submitted_at: null,
turnitin_data: null,
updated_at: '2013-12-16T21:25:45Z',
url: null,
user_id: '3',
workflow_state: 'graded',
submission_history: [
{
submission: {
assignment_id: 1,
attachment_id: null,
attachment_ids: null,
attempt: null,
body: null,
cached_due_date: '2013-12-19T06:59:59Z',
context_code: 'course_2',
created_at: '2013-12-12T22:57:34Z',
grade: '100',
grade_matches_current_submission: true,
graded_at: '2013-12-16T21:25:44Z',
grader_id: 1,
group_id: null,
has_admin_comment: false,
has_rubric_assessment: null,
id: 34291,
media_comment_id: null,
media_comment_type: null,
media_object_id: null,
process_attempts: 0,
processed: null,
published_grade: '150',
published_score: 100.0,
quiz_submission_id: null,
score: 100.0,
student_entered_score: null,
submission_comments_count: null,
submission_type: null,
submitted_at: null,
turnitin_data: null,
updated_at: '2013-12-16T21:25:45Z',
url: null,
user_id: 3,
workflow_state: 'graded',
versioned_attachments: [],
},
},
],
submission_comments: [],
attachments: [],
},
},
]
const students = [
{
user_id: '1',
user: {id: '1', name: 'Bob Barnes', group_ids: [], sections: [], sortable_name: 'Barnes, Bob'},
course_section_id: '1',
},
{
user_id: '2',
user: {id: '2', name: 'Fred Flint', group_ids: [], sections: [], sortable_name: 'Flint, Fred'},
course_section_id: '1',
},
{
user_id: '3',
user: {id: '3', name: 'Suzy Smith', group_ids: [], sections: [], sortable_name: 'Smith, Suzy'},
course_section_id: '1',
},
{
user_id: '4',
user: {
id: '4',
name: 'Buffy Baker',
group_ids: [],
sections: [],
sortable_name: 'Baker, Buffy',
},
course_section_id: '2',
},
{
user_id: '5',
user: {
id: '5',
name: 'Willow West',
group_ids: [],
sections: [],
sortable_name: 'West, Willow',
},
course_section_id: '2',
},
{
user_id: '5',
user: {
id: '5',
name: 'Willow West',
group_ids: [],
sections: [],
sortable_name: 'West, Willow',
},
course_section_id: '1',
},
{
user_id: '6',
user: {
id: '6',
name: 'Giles Green',
group_ids: [],
sections: [],
sortable_name: 'Green, Giles',
},
course_section_id: '2',
},
{
user_id: '7',
user: {
id: '7',
name: 'Xander Xiao',
group_ids: [],
sections: [],
sortable_name: 'Xiao, Xander',
},
course_section_id: '2',
},
{
user_id: '8',
user: {
id: '8',
name: 'Cordelia Lu',
group_ids: [],
sections: [],
sortable_name: 'Lu, Cordelia',
},
course_section_id: '2',
},
{
user_id: '9',
user: {
id: '9',
name: 'Drusilla Da',
group_ids: [],
sections: [],
sortable_name: 'Da, Drusilla',
},
course_section_id: '1',
},
{
user_id: '10',
user: {id: '10', name: 'Spike Long', group_ids: [], sections: [], sortable_name: 'Long, Spike'},
course_section_id: '2',
},
{
user_id: '10',
user: {id: '10', name: 'Spike Long', group_ids: [], sections: [], sortable_name: 'Long, Spike'},
course_section_id: '1',
},
]
const concludedStudents = [
{
user: {id: '105', name: 'Lyra', group_ids: [], sections: []},
course_section_id: '1',
user_id: '105',
workflow_state: 'completed',
completed_at: '2013-10-01T10:00:00Z',
},
]
const assignmentGroups = [
{
id: '1',
name: 'AG1',
position: 1,
group_weight: 0,
assignments: [
{
id: '1',
name: 'Z Eats Soup',
points_possible: 100,
grading_type: 'points',
submission_types: ['none'],
due_at: '2013-10-01T10:00:00Z',
position: 1,
assignment_group_id: '1',
published: true,
muted: false,
only_visible_to_overrides: true,
assignment_visibility: ['1'],
},
{
id: '2',
name: 'Drink Water',
grading_type: 'points',
points_possible: null,
due_at: null,
position: 10,
submission_types: ['online_url', 'online_text_entry'],
assignment_group_id: '1',
published: true,
muted: true,
only_visible_to_overrides: true,
assignment_visibility: ['2'],
},
{
id: '3',
name: 'Apples are good',
points_possible: 1000,
grading_type: 'points',
submission_types: ['none'],
due_at: '2013-12-01T10:00:00Z',
position: 12,
assignment_group_id: '1',
published: true,
muted: false,
assignment_visibility: ['1', '2', '3'],
},
],
},
{
id: '2',
name: 'AG2',
position: 10,
group_weight: 0,
assignments: [
{
id: '4',
name: 'Big Bowl of Nachos',
points_possible: 20,
grading_type: 'percent',
submission_types: ['none'],
due_at: null,
position: 5,
assignment_group_id: '2',
published: true,
muted: false,
},
{
id: '5',
name: 'Can You Eat Just One?',
points_possible: 40,
grading_type: 'percent',
submission_types: ['none'],
due_at: '2013-08-01T10:00:00Z',
position: 6,
assignment_group_id: '2',
published: true,
muted: true,
},
{
id: '6',
name: 'Da Fish and Chips!',
points_possible: 40,
grading_type: 'pass_fail',
submission_types: ['none'],
due_at: '2013-09-01T10:00:00Z',
position: 9,
published: true,
assignment_group_id: '2',
},
],
},
{
id: '4',
name: 'Silent Assignments',
position: 2,
group_weight: 0,
assignments: [
{
id: '20',
name: 'Published Assignment',
points_possible: 10,
grading_type: 'percent',
submission_types: ['none'],
due_at: '2013-09-01T10:00:00Z',
position: 5,
assignment_group_id: '4',
published: true,
},
{
id: '22',
name: 'Not Graded',
points_possible: 10,
grading_type: 'percent',
submission_types: ['not_graded'],
due_at: '2013-09-01T10:00:00Z',
position: 1,
assignment_group_id: '4',
published: true,
},
],
},
{
id: '5',
name: 'Invalid AG',
position: 3,
group_weight: 0,
assignments: [
{
id: '24',
name: 'No Points Assignment',
points_possible: 0,
grading_type: 'percent',
submission_types: ['not_graded'],
due_at: '2013-09-01T10:00:00Z',
position: 1,
assignment_group_id: '4',
published: true,
},
],
},
{
id: '6',
name: 'moderated grading',
position: 4,
group_weight: 0,
assignments: [
{
id: '25',
name: 'muted, moderated, grades not published',
points_possible: 10,
grading_type: 'percent',
submission_types: ['not_graded'],
due_at: '2013-09-01T10:00:00Z',
position: 1,
assignment_group_id: '4',
muted: true,
moderated_grading: true,
grades_published: false,
published: true,
},
{
id: '26',
name: 'not muted, moderated, grades not published',
points_possible: 10,
grading_type: 'percent',
submission_types: ['not_graded'],
due_at: '2013-09-01T10:00:00Z',
position: 1,
assignment_group_id: '4',
muted: false,
moderated_grading: true,
grades_published: false,
published: true,
},
{
id: '27',
name: 'muted, not moderated, grades not published',
points_possible: 10,
grading_type: 'percent',
submission_types: ['not_graded'],
due_at: '2013-09-01T10:00:00Z',
position: 1,
assignment_group_id: '4',
muted: true,
moderated_grading: false,
grades_published: false,
published: true,
},
{
id: '28',
name: 'muted, moderated, grades published',
points_possible: 10,
grading_type: 'percent',
submission_types: ['not_graded'],
due_at: '2013-09-01T10:00:00Z',
position: 1,
assignment_group_id: '4',
muted: true,
moderated_grading: true,
grades_published: true,
published: true,
},
],
},
]
const submissions = [
{
user_id: '1',
submissions: [
{id: '1', user_id: '1', assignment_id: '1', grade: '3', score: '3'},
{id: '2', user_id: '1', assignment_id: '2', grade: null, score: null},
{
id: '5',
user_id: '1',
assignment_id: '6',
grade: 'incomplete',
score: 'incomplete',
entered_grade: 'incomplete',
entered_score: 'incomplete',
},
],
},
{
user_id: '2',
submissions: [
{id: '3', user_id: '2', assignment_id: '1', grade: '9', score: '9'},
{id: '4', user_id: '2', assignment_id: '2', grade: null, score: null},
],
},
{
user_id: '3',
submissions: [
{id: '5', user_id: '3', assignment_id: '1', grade: '10', score: '10'},
{id: '6', user_id: '3', assignment_id: '2', grade: null, score: null},
],
},
{
user_id: '4',
submissions: [],
},
{
user_id: '5',
submissions: [],
},
{
user_id: '6',
submissions: [],
},
{
user_id: '7',
submissions: [],
},
{
user_id: '8',
submissions: [],
},
{
user_id: '9',
submissions: [],
},
{
user_id: '10',
submissions: [],
},
]
const sections = [
{id: '1', name: 'Vampires and Demons'},
{id: '2', name: 'Slayers and Scoobies'},
]
const customColumns = [
{
hidden: false,
id: '1',
position: 1,
teacher_notes: true,
title: 'Notes',
},
]
const outcomesRaw = [
{outcome: {id: '1', title: 'Eating', mastery_points: 3}},
{outcome: {id: '2', title: 'Drinking', mastery_points: 5}},
]
const outcomes = [
{id: '1', title: 'Eating', mastery_points: 3},
{id: '2', title: 'Drinking', mastery_points: 5},
]
const outcomeRollupsRaw = {
rollups: [
{
links: {user: '1'},
scores: [
{links: {outcome: '1'}, score: 5},
{links: {outcome: '2'}, score: 4},
{links: {outcome: '3'}, score: 5},
],
},
{
links: {user: '2'},
scores: [{links: {outcome: '2'}, score: 3}],
},
],
}
const outcomeRollups = [
{outcome_id: '1', user_id: '1', score: 5},
{outcome_id: '2', user_id: '1', score: 4},
{outcome_id: '2', user_id: '2', score: 3},
]
export default {
custom_columns: customColumns,
set_default_grade_response: default_grade_response,
students,
concluded_enrollments: concludedStudents,
assignment_groups: assignmentGroups,
submissions,
sections,
outcomes,
outcome_rollups: outcomeRollups,
create() {
window.ENV = {
current_user_id: 1,
context_asset_string: 'course_1',
GRADEBOOK_OPTIONS: {
enrollments_url: '/api/v1/enrollments',
enrollments_with_concluded_url: '/api/v1/concluded_enrollments',
assignment_groups_url: '/api/v1/assignment_groups',
submissions_url: '/api/v1/submissions',
sections_url: '/api/v1/sections',
context_url: '/courses/1',
context_id: 1,
group_weighting_scheme: 'equal',
change_grade_url: '/api/v1/courses/1/assignments/:assignment/submissions/:submission',
custom_columns_url: 'api/v1/courses/1/custom_gradebook_columns',
custom_column_data_url: 'api/v1/courses/1/custom_gradebook_columns/:id',
setting_update_url: 'api/v1/courses/1/settings',
outcome_gradebook_enabled: true,
outcome_links_url: 'api/v1/courses/1/outcome_group_links',
outcome_rollups_url: 'api/v1/courses/1/outcome_rollups',
active_grading_periods: [
{
id: '1',
title: 'Fall Period 1',
start_date: '2013-08-01T00:00:00Z',
end_date: '2013-09-01T00:00:00Z',
close_date: '2013-09-08T00:00:00Z',
is_closed: true,
},
{
id: '2',
title: 'Fall Period 2',
start_date: '2013-09-01T00:00:00Z',
end_date: '2013-11-01T00:00:00Z',
close_date: '2013-11-08T00:00:00Z',
is_closed: true,
},
{
id: '3',
title: 'Fall Period 3',
start_date: '2013-11-01T00:00:00Z',
end_date: '2014-01-01T00:00:00Z',
close_date: '2014-01-08T00:00:00Z',
is_closed: false,
},
],
},
}
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.enrollments_url, {
response: clone(students),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.enrollments_with_concluded_url, {
response: clone(concludedStudents),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.assignment_groups_url, {
response: clone(assignmentGroups),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.submissions_url, {
response: clone(submissions),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.sections_url, {
response: clone(sections),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.custom_columns_url, {
response: clone(customColumns),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
// the qUnit specs were sending a request for this that was 404'ing so I just
// added this so it wouldn't
ajax.defineFixture(`${window.ENV.GRADEBOOK_OPTIONS.custom_columns_url}/1`, {
response: {},
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.setting_update_url, {
response: true,
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.outcome_links_url, {
response: clone(outcomesRaw),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
return ajax.defineFixture(window.ENV.GRADEBOOK_OPTIONS.outcome_rollups_url, {
response: clone(outcomeRollupsRaw),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
},
}

View File

@ -1,56 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import startApp from './start_app'
import Ember from 'ember'
import fixtures from './ajax_fixtures'
let App = null
QUnit.module('screenreader_gradebook', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(
() => (this.controller = App.__container__.lookup('controller:screenreader_gradebook'))
)
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('fetches enrollments', function () {
equal(this.controller.get('enrollments').objectAt(0).user.name, 'Bob Barnes')
equal(this.controller.get('enrollments').objectAt(1).user.name, 'Fred Flint')
})
test('fetches sections', function () {
equal(this.controller.get('sections').objectAt(0).name, 'Vampires and Demons')
equal(this.controller.get('sections').objectAt(1).name, 'Slayers and Scoobies')
})
test('fetches custom_columns', function () {
equal(this.controller.get('custom_columns.length'), 1)
equal(this.controller.get('custom_columns.firstObject').title, fixtures.custom_columns[0].title)
})
test('fetches outcomes', function () {
equal(this.controller.get('outcomes').objectAt(0).title, 'Eating')
equal(this.controller.get('outcomes').objectAt(1).title, 'Drinking')
})

View File

@ -1,166 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import Ember from 'ember'
import startApp from '../start_app'
import fixtures from '../ajax_fixtures'
const {run} = Ember
const groupScores = {
assignment_group_1: {
possible: 1000,
score: 946.65,
submission_count: 10,
submissions: [],
weight: 90,
},
}
const periodScores = {
grading_period_1: {
possible: 1800.111,
score: 95.1225,
submission_count: 30,
submissions: [],
weight: 60,
},
}
QUnit.module('assignment_subtotal_grades_component by group', {
setup() {
fixtures.create()
const App = startApp()
this.component = App.AssignmentSubtotalGradesComponent.create()
this.component.reopen({
gradingStandard: function () {
return [
['A', 0.5],
['C', 0.05],
['F', 0.0],
]
}.property(),
weightingScheme: function () {
return 'percent'
}.property(),
})
return run(() => {
this.assignment_group = Ember.copy(fixtures.assignment_groups, true).findBy('id', '1')
this.student = Ember.Object.create(Ember.copy(groupScores))
return this.component.setProperties({
student: this.student,
subtotal: {
name: this.assignment_group.name,
weight: this.assignment_group.group_weight,
key: `assignment_group_${this.assignment_group.id}`,
},
})
})
},
teardown() {
return run(() => {
this.component.destroy()
return App.destroy()
})
},
})
test('values', function () {
deepEqual(this.component.get('values'), groupScores.assignment_group_1)
})
test('points', function () {
const expected = '946.65 / 1,000'
equal(this.component.get('points'), expected)
})
test('percent', function () {
const expected = '94.67%'
strictEqual((946.65 / 1000) * 100, 94.66499999999999)
strictEqual(this.component.get('percent'), expected)
})
test('letterGrade', function () {
const expected = 'A'
equal(this.component.get('letterGrade'), expected)
})
test('scoreDetail', function () {
const expected = '(946.65 / 1,000)'
equal(this.component.get('scoreDetail'), expected)
})
QUnit.module('assignment_subtotal_grades_component by period', {
setup() {
fixtures.create()
const App = startApp()
this.component = App.AssignmentSubtotalGradesComponent.create()
this.component.reopen({
gradingStandard: function () {
return [
['A', 0.5],
['C', 0.05],
['F', 0.0],
]
}.property(),
})
return run(() => {
this.student = Ember.Object.create(Ember.copy(periodScores))
return this.component.setProperties({
student: this.student,
subtotal: {
name: 'Grading Period 1',
weight: 0.65,
key: 'grading_period_1',
},
})
})
},
teardown() {
return run(() => {
this.component.destroy()
return App.destroy()
})
},
})
test('values', function () {
deepEqual(this.component.get('values'), periodScores.grading_period_1)
})
test('points', function () {
const expected = '95.12 / 1,800.11'
equal(this.component.get('points'), expected)
})
test('percent', function () {
const expected = '5.28%'
equal(this.component.get('percent'), expected)
})
test('letterGrade', function () {
const expected = 'C'
equal(this.component.get('letterGrade'), expected)
})
test('scoreDetail', function () {
const expected = '(95.12 / 1,800.11)'
equal(this.component.get('scoreDetail'), expected)
})

View File

@ -1,80 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import Ember from 'ember'
import startApp from '../start_app'
import fixtures from '../ajax_fixtures'
const {run} = Ember
const groupScores = {
assignment_group_1: {
possible: 100,
score: 54.5,
submission_count: 1,
submissions: [],
weight: 100,
},
}
QUnit.module('assignment_subtotal_grades_component_letter_grade', {
setup() {
fixtures.create()
const App = startApp()
this.component = App.AssignmentSubtotalGradesComponent.create()
this.component.reopen({
gradingStandard: function () {
return [
['A', 0.8],
['B+', 55.5],
['B', 54.5],
['C', 0.05],
['F', 0.0],
]
}.property(),
weightingScheme: function () {
return 'percent'
}.property(),
})
return run(() => {
this.assignment_group = Ember.copy(fixtures.assignment_groups, true).findBy('id', '1')
this.student = Ember.Object.create(Ember.copy(groupScores))
return this.component.setProperties({
student: this.student,
subtotal: {
name: this.assignment_group.name,
key: `assignment_group_${this.assignment_group.id}`,
weight: this.assignment_group.group_weight,
},
})
})
},
teardown() {
return run(() => {
this.component.destroy()
return App.destroy()
})
},
})
test('letterGrade', function () {
const expected = 'C'
equal(this.component.get('letterGrade'), expected)
})

View File

@ -1,150 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import 'jquery-migrate'
import Ember from 'ember'
import startApp from '../start_app'
import fixtures from '../ajax_fixtures'
import GradebookHelpers from '../../../helpers'
import GradebookConstants from '../../../constants'
const {run} = Ember
QUnit.module('custom_column_cell', {
setup() {
fixtures.create()
const App = startApp()
this.component = App.CustomColumnCellComponent.create()
this.component.reopen({
customColURL() {
return '/api/v1/custom_gradebook_columns/:id/:user_id'
},
})
return run(() => {
this.column = Ember.Object.create({
id: '22',
title: 'Notes',
read_only: false,
is_loading: false,
})
this.student = Ember.Object.create({
id: '45',
})
this.dataForStudent = [
Ember.Object.create({
column_id: '22',
content: 'lots of content here',
}),
]
this.component.setProperties({
student: this.student,
column: this.column,
dataForStudent: this.dataForStudent,
})
return this.component.append()
})
},
teardown() {
return run(() => {
this.component.destroy()
return App.destroy()
})
},
})
test('id', function () {
equal(this.component.get('id'), 'custom_col_22')
})
test('value', function () {
equal(this.component.get('value'), 'lots of content here')
})
test('saveUrl', function () {
equal(this.component.get('saveURL'), '/api/v1/custom_gradebook_columns/22/45')
})
test('disabled is true when column isLoading', function () {
this.component.column.set('isLoading', true)
this.component.column.set('read_only', false)
equal(this.component.get('disabled'), true)
})
test('disabled is true when column is read_only', function () {
this.component.column.set('isLoading', false)
this.component.column.set('read_only', true)
equal(this.component.get('disabled'), true)
})
test('disabled is false when column is not loading and not read_only', function () {
this.component.column.set('isLoading', false)
this.component.column.set('read_only', false)
equal(this.component.get('disabled'), false)
})
test('focusOut', function (assert) {
assert.expect(1)
const stub = sandbox.stub(this.component, 'boundSaveSuccess')
let requestStub = null
run(
() =>
(requestStub = Ember.RSVP.resolve({
id: '22',
title: 'Notes',
content: 'less content now',
}))
)
sandbox.stub(this.component, 'ajax').returns(requestStub)
run(() => {
this.component.set('value', 'such success')
return this.component.send('focusOut')
})
ok(stub.called)
})
test('textAreaInput does not flash an error if note length is exactly the max allowed length', function () {
const note = 'a'.repeat(GradebookConstants.MAX_NOTE_LENGTH)
const noteInputEvent = {target: {value: note}}
const maxLengthError = sandbox.spy(GradebookHelpers, 'flashMaxLengthError')
this.component.textAreaInput(noteInputEvent)
ok(maxLengthError.notCalled)
})
test('textAreaInput flashes an error if note length is greater than the max allowed length', function () {
const note = 'a'.repeat(GradebookConstants.MAX_NOTE_LENGTH + 1)
const noteInputEvent = {target: {value: note}}
const maxLengthError = sandbox.spy(GradebookHelpers, 'flashMaxLengthError')
this.component.textAreaInput(noteInputEvent)
ok(maxLengthError.calledOnce)
})
test('textAreaInput does not flash an error if there is already an error showing, even if note length is greater than the max allowed length', function () {
const note = 'a'.repeat(GradebookConstants.MAX_NOTE_LENGTH + 1)
const noteInputEvent = {target: {value: note}}
const maxLengthError = sandbox.spy(GradebookHelpers, 'flashMaxLengthError')
const findStub = sandbox.stub($, 'find')
findStub.withArgs('.ic-flash-error').returns(['a non-empty error array'])
this.component.textAreaInput(noteInputEvent)
ok(maxLengthError.notCalled)
})

View File

@ -1,42 +0,0 @@
//
// Copyright (C) 2018 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import startApp from '../start_app'
QUnit.module('FinalGradeComponent', hooks => {
let App
let component
hooks.beforeEach(() => {
App = startApp()
component = App.FinalGradeComponent.create({})
})
test('bubbles up an onEditFinalGradeOverride action with the grade', () => {
const targetObject = {
onEditFinalGradeOverride(grade) {
strictEqual(grade, '93%')
},
}
component.set('onEditFinalGradeOverride', 'onEditFinalGradeOverride')
component.set('targetObject', targetObject)
component.send('onEditFinalGradeOverride', '93%')
})
})

View File

@ -1,96 +0,0 @@
//
// Copyright (C) 2018 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import startApp from '../start_app'
QUnit.module('FinalGradeOverrideComponent', hooks => {
let App
let component
let finalGradeOverride
hooks.beforeEach(() => {
App = startApp()
finalGradeOverride = {
percentage: 92.32,
}
const gradingStandard = [
['A', 0.9],
['B', 0.8],
['C', 0.0],
]
component = App.FinalGradeOverrideComponent.create({finalGradeOverride, gradingStandard})
})
test('inputValue returns the override grade', () => {
strictEqual(component.get('inputValue'), 'A')
})
test('inputValue returns the percentage when grading schemes are not enabled', () => {
component.set('gradingStandard', undefined)
strictEqual(component.get('inputValue'), '92.32%')
})
test('inputDescription returns a formatted percentage', () => {
strictEqual(component.get('inputDescription'), '92.32%')
})
test('inputDescription returns null when grading schemes are not enabled', () => {
component.set('gradingStandard', undefined)
strictEqual(component.get('inputDescription'), null)
})
test('changing the override percentage changes the inputValue', () => {
finalGradeOverride.percentage = 86.7
component.set('finalGradeOverride', {...finalGradeOverride})
strictEqual(component.get('inputValue'), 'B')
})
test('changing the override percentage changes the inputDescription', () => {
finalGradeOverride.percentage = 86.7
strictEqual(component.get('inputDescription'), '86.7%')
})
test('changing the grading standard changes the inputValue', () => {
const gradingStandard = [
['A', 0.99],
['F', 0.0],
]
component.set('gradingStandard', gradingStandard)
strictEqual(component.get('inputValue'), 'F')
})
test('focusOut sends onEditFinalGradeOverride with the inputValue', () => {
const targetObject = {
onEditFinalGradeOverride(grade) {
strictEqual(grade, 92.1)
},
}
component.set('onEditFinalGradeOverride', 'onEditFinalGradeOverride')
component.set('targetObject', targetObject)
component.set('inputValue', 92.1)
component.focusOut()
})
test('focusOut sets the inputValue to the internalInputValue', () => {
component.set('internalInputValue', 'C')
component.focusOut()
strictEqual(component.get('inputValue'), 'C')
})
})

View File

@ -1,197 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import 'jquery-migrate'
import Ember from 'ember'
import * as tz from '@instructure/moment-utils'
import startApp from '../start_app'
import fixtures from '../ajax_fixtures'
import GradeFormatHelper from '@canvas/grading/GradeFormatHelper'
const {run} = Ember
let setType = null
QUnit.module('grading_cell', {
setup() {
window.ENV = {}
fixtures.create()
const App = startApp()
this.component = App.GradingCellComponent.create()
ENV.GRADEBOOK_OPTIONS.grading_period_set = {
id: '1',
weighted: false,
display_totals_for_all_grading_periods: false,
}
ENV.current_user_roles = []
setType = type => run(() => this.assignment.set('grading_type', type))
this.component.reopen({
changeGradeURL() {
return '/api/v1/assignment/:assignment/:submission'
},
})
return run(() => {
this.submission = Ember.Object.create({
grade: 'B',
entered_grade: 'A',
score: 8,
entered_score: 10,
points_deducted: 2,
gradeLocked: false,
assignment_id: 1,
user_id: 1,
})
this.assignment = Ember.Object.create({
due_at: tz.parse('2013-10-01T10:00:00Z'),
grading_type: 'points',
points_possible: 10,
})
this.component.setProperties({
submission: this.submission,
assignment: this.assignment,
})
return this.component.append()
})
},
teardown() {
return run(() => {
this.component.destroy()
App.destroy()
return (window.ENV = {})
})
},
})
test('setting value on init', function () {
const component = App.GradingCellComponent.create()
equal(component.get('value'), '-')
equal(this.component.get('value'), 'A')
})
test('entered_score', function () {
equal(this.component.get('entered_score'), 10)
})
test('late_penalty', function () {
equal(this.component.get('late_penalty'), -2)
})
test('points_possible', function () {
equal(this.component.get('points_possible'), 10)
})
test('final_grade', function () {
equal(this.component.get('final_grade'), 'B')
})
test('saveURL', function () {
equal(this.component.get('saveURL'), '/api/v1/assignment/1/1')
})
test('isPoints', function () {
setType('points')
ok(this.component.get('isPoints'))
})
test('isPercent', function () {
setType('percent')
ok(this.component.get('isPercent'))
})
test('isLetterGrade', function () {
setType('letter_grade')
ok(this.component.get('isLetterGrade'))
})
test('isInPastGradingPeriodAndNotAdmin is true when the submission is gradeLocked', function () {
run(() => this.submission.set('gradeLocked', true))
equal(this.component.get('isInPastGradingPeriodAndNotAdmin'), true)
})
test('isInPastGradingPeriodAndNotAdmin is false when the submission is not gradeLocked', function () {
run(() => this.submission.set('gradeLocked', false))
equal(this.component.get('isInPastGradingPeriodAndNotAdmin'), false)
})
test('nilPointsPossible', function () {
run(() => this.assignment.set('points_possible', null))
ok(this.component.get('nilPointsPossible'))
run(() => this.assignment.set('points_possible', 10))
equal(this.component.get('nilPointsPossible'), false)
})
test('isGpaScale', function () {
setType('gpa_scale')
ok(this.component.get('isGpaScale'))
})
test('isPassFail', function () {
setType('pass_fail')
ok(this.component.get('isPassFail'))
})
test('does not translate pass_fail grades', function () {
setType('pass_fail')
sandbox.stub(GradeFormatHelper, 'formatGrade').returns('completo')
run(() => this.submission.set('entered_grade', 'complete'))
this.component.submissionDidChange()
equal(this.component.get('value'), 'complete')
})
test('formats percent grades', function () {
setType('percent')
sandbox.stub(GradeFormatHelper, 'formatGrade').returns('32,4%')
run(() => this.submission.set('entered_grade', '32.4'))
this.component.submissionDidChange()
equal(this.component.get('value'), '32,4%')
})
test('focusOut', function (assert) {
const done = assert.async()
const stub = sandbox.stub(this.component, 'boundUpdateSuccess')
const submissions = []
let requestStub = null
run(() => {
requestStub = Ember.RSVP.resolve({all_submissions: submissions})
})
sandbox.stub(this.component, 'ajax').returns(requestStub)
run(() => {
this.component.set('value', '10')
return this.component.send('focusOut', {target: {id: 'student_and_assignment_grade'}})
})
// eslint-disable-next-line promise/catch-or-return
Promise.resolve().then(() => {
ok(stub.called)
// eslint-disable-next-line promise/no-callback-in-promise
done()
})
})
test('onUpdateSuccess', function () {
run(() => this.assignment.set('points_possible', 100))
const flashWarningStub = sandbox.stub($, 'flashWarning')
this.component.onUpdateSuccess({all_submissions: [], score: 150})
ok(flashWarningStub.called)
})

View File

@ -1,95 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import {extend} from 'lodash'
import DownloadDialog from '../../components/ic_submission_download_dialog_component'
import fixtures from '../shared_ajax_fixtures'
function buildComponent(props) {
props = extend(props, {assignmentUrl: '/courses/1/assignments/1'})
return DownloadDialog.create(props)
}
QUnit.module('ic_submission_download_dialog', {
setup() {
return fixtures.create()
},
})
test('is "finished" if file ready', () => {
const component = buildComponent({
attachment: {workflow_state: 'available'},
})
equal(component.get('status'), 'finished')
})
test('is "zipping" if percent complete is > 95', () => {
const component = buildComponent({
percentComplete: 95,
})
equal(component.get('status'), 'zipping')
})
test('is "starting" otherwise', () => {
const component = buildComponent()
equal(component.get('status'), 'starting')
})
QUnit.module('progress:')
test('percentComplete is 100 if file ready', () => {
// initialize percentComplete at 100
// so the observer that updates the progressbar doesn't fire
// otherwise the test fails because there is no DOM
const component = buildComponent({
attachment: {workflow_state: 'available'},
percentComplete: 100,
})
component.progress()
equal(component.get('percentComplete'), 100)
})
test('percentComplete is 0 if file_state is a string', () => {
const component = buildComponent({
attachment: {file_state: 'ready_to_download'},
})
component.progress()
equal(component.get('percentComplete'), 0)
})
QUnit.module('keepChecking')
test('is true if open', () => {
const component = buildComponent({
isOpened: true,
})
equal(component.get('keepChecking'), true)
})
test('is undefined if closed', () => {
const component = buildComponent()
equal(component.get('keepChecking'), undefined)
})
test('is undefined if percentComplete is 100', () => {
const component = buildComponent({
percentComplete: 100,
})
equal(component.get('keepChecking'), undefined)
})

View File

@ -1,104 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import Ember from 'ember'
import startApp from '../start_app'
import fixtures from '../ajax_fixtures'
const {run} = Ember
let mutedAssignment = null
let unmutedAssignment = null
function compareIdentity(assignment, fixture) {
equal(assignment.muted, fixture.muted, 'muted status')
equal(assignment.id, fixture.id, 'assignment id')
}
QUnit.module('screenreader_gradebook assignment_muter_component', hooks => {
hooks.beforeEach(function (_assert) {
fixtures.create()
mutedAssignment = fixtures.assignment_groups[0].assignments[1]
unmutedAssignment = fixtures.assignment_groups[0].assignments[0]
const App = startApp()
return run(() => {
this.assignment = Ember.copy(mutedAssignment, true)
return (this.component = App.AssignmentMuterComponent.create({assignment: this.assignment}))
})
})
hooks.afterEach(function (_assert) {
return run(() => {
this.component.destroy()
return (this.component = null)
})
})
QUnit.test('it works', function () {
compareIdentity(this.component.get('assignment'), mutedAssignment)
Ember.run(() => {
this.assignment = Ember.copy(unmutedAssignment, true)
return this.component.setProperties({
assignment: this.assignment,
})
})
compareIdentity(this.component.get('assignment'), unmutedAssignment)
})
QUnit.module('moderated grading', _hooks => {
QUnit.test('it is not disabled if assignment is not muted', function () {
unmutedAssignment = fixtures.assignment_groups[4].assignments[1]
Ember.run(() => {
this.assignment = Ember.copy(unmutedAssignment, true)
return (this.component = App.AssignmentMuterComponent.create({assignment: this.assignment}))
})
strictEqual(this.component.get('disabled'), false)
})
QUnit.test('it is not disabled if assignment is not moderated', function () {
const nonmoderatedAssignment = fixtures.assignment_groups[4].assignments[2]
Ember.run(() => {
this.assignment = Ember.copy(nonmoderatedAssignment, true)
return (this.component = App.AssignmentMuterComponent.create({assignment: this.assignment}))
})
strictEqual(this.component.get('disabled'), false)
})
QUnit.test('it is not disabled if assignment has grades published', function () {
const gradesPublishedAssignment = fixtures.assignment_groups[4].assignments[3]
Ember.run(() => {
this.assignment = Ember.copy(gradesPublishedAssignment, true)
return (this.component = App.AssignmentMuterComponent.create({assignment: this.assignment}))
})
strictEqual(this.component.get('disabled'), false)
})
QUnit.test('it is disabled if muted, moderated, and grades not published', function () {
const mutedModeratedGradesNotPublishedAssignment =
fixtures.assignment_groups[4].assignments[0]
Ember.run(() => {
this.assignment = Ember.copy(mutedModeratedGradesNotPublishedAssignment, true)
return (this.component = App.AssignmentMuterComponent.create({assignment: this.assignment}))
})
strictEqual(this.component.get('disabled'), true)
})
})
})

View File

@ -1,53 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import startApp from '../start_app'
import Ember from 'ember'
import fixtures from '../ajax_fixtures'
let App = null
QUnit.module('grading_cell_component integration test for isPoints', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
this.assignment = this.controller.get('assignments').findBy('id', '6')
this.student = this.controller.get('students').findBy('id', '1')
return Ember.run(() =>
this.controller.setProperties({
submissions: Ember.copy(fixtures.submissions, true),
selectedAssignment: this.assignment,
selectedStudent: this.student,
})
)
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
// unskip in EVAL-2505
QUnit.skip('fast-select instance is used for grade input', () => {
ok(find('#student_and_assignment_grade').is('select'))
equal(find('#student_and_assignment_grade').val(), 'incomplete')
})

View File

@ -1,534 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import 'jquery-migrate'
import startApp from '../start_app'
import Ember from 'ember'
import fixtures from '../ajax_fixtures'
let App = null
const buttonDisabled = (trigger, expectedBoolean) =>
equal(find(trigger).prop('disabled'), expectedBoolean)
const checkSelection = (id, selection) => equal(id, find(selection).val())
const checkSelectedText = (text, selection) =>
equal(text, find(selection).find('option:selected').text())
const checkText = (selector, expectedText) =>
equal(Ember.$.trim(find(`.assignmentsPanel ${selector}`).text()), expectedText)
function studentSectionAssertions(selected, currentIndex, expectedIndex) {
equal(currentIndex, expectedIndex)
checkSelection(selected.id, '#student_select')
return checkSelectedText(selected.sortable_name, '#student_select')
}
QUnit.module('screenreader_gradebook student/assignment navigation: on page load', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(
() => (this.controller = App.__container__.lookup('controller:screenreader_gradebook'))
)
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('Previous Student button is disabled', () =>
buttonDisabled('.student_navigation .previous_object:first', true))
test('Previous Assignment button is disabled', () =>
buttonDisabled('.assignment_navigation .previous_object', true))
test('Next Student button is active', () =>
buttonDisabled('.student_navigation .next_object:first', false))
test('Next Assignment button is active', () =>
buttonDisabled('.assignment_navigation .next_object', false))
test('no student or assignment is loaded', () => {
checkText('.student_selection', 'Select a student to view additional information here.')
return checkText(
'.assignment_selection',
'Select an assignment to view additional information here.'
)
})
QUnit.module('screenreader_gradebook student/assignment navigation: with first item selected', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return Ember.run(() => {
this.controller.set('selectedStudent', this.controller.get('students.firstObject'))
return this.controller.set(
'selectedAssignment',
this.controller.get('assignments.firstObject')
)
})
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('Previous buttons are disabled', () => {
buttonDisabled('.student_navigation .previous_object:first', true)
buttonDisabled('.assignment_navigation .previous_object', true)
checkText('.student_selection', 'Bob Barnes')
return checkText('.assignment_selection', 'Z Eats Soup')
})
// compares & checks before/after objects
test('clicking Next Student button displays next student', function () {
const before = this.controller.get('selectedStudent')
checkSelection(before.id, '#student_select')
return click('.student_navigation .next_object:first').then(() => {
const after = this.controller.get('selectedStudent')
checkSelection(after.id, '#student_select')
notEqual(before.id, after.id)
const next = this.controller.get('students').indexOf(before) + 1
equal(next, this.controller.get('students').indexOf(after))
})
})
// compares & checks before/after objects
test('clicking Next Assignment button displays next assignment', function () {
const before = this.controller.get('selectedAssignment')
checkSelection(before.id, '#assignment_select')
return click('.assignment_navigation .next_object').then(() => {
const after = this.controller.get('selectedAssignment')
checkSelection(after.id, '#assignment_select')
notEqual(before, after)
const next = this.controller.get('assignments').indexOf(before) + 1
equal(next, this.controller.get('assignments').indexOf(after))
})
})
test('clicking next then previous will refocus on next student', () =>
click('.student_navigation .next_object:first').then(() =>
click('.student_navigation .previous_object:first').then(() => {
equal($('.student_navigation .next_object:first')[0], document.activeElement)
})
))
test('clicking next then previous will refocus on next assignment', () =>
click('.assignment_navigation .next_object').then(() =>
click('.assignment_navigation .previous_object').then(() => {
equal($('.assignment_navigation .next_object')[0], document.activeElement)
})
))
QUnit.module('screenreader_gradebook student/assignment navigation: with second item selected', {
setup() {
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return Ember.run(() => {
this.controller.set('selectedStudent', this.controller.get('students').objectAt(1))
return this.controller.set(
'selectedAssignment',
this.controller.get('assignments').objectAt(1)
)
})
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('Previous/Next Student buttons are both active', () => {
buttonDisabled('.student_navigation .previous_object:first', false)
return buttonDisabled('.student_navigation .next_object:first', false)
})
test('Previous/Next Assignment buttons are both active', () => {
buttonDisabled('.assignment_navigation .previous_object', false)
return buttonDisabled('.assignment_navigation .next_object', false)
})
QUnit.module('screenreader_gradebook student/assignment navigation: with last item selected', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return Ember.run(() => {
this.controller.set('selectedStudent', this.controller.get('students.lastObject'))
return this.controller.set(
'selectedAssignment',
this.controller.get('assignments.lastObject')
)
})
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('Previous Student button is active', () =>
buttonDisabled('.student_navigation .previous_object:first', false))
test('Previous Assignment button is active', () =>
buttonDisabled('.assignment_navigation .previous_object', false))
test('Next Student button is disabled', () =>
buttonDisabled('.student_navigation .next_object:first', true))
test('Next Assignment button is disabled', () =>
buttonDisabled('.assignment_navigation .next_object', true))
// compares & checks before/after objects
test('clicking Previous Student button displays previous student', function () {
const before = this.controller.get('selectedStudent')
checkSelection(before.id, '#student_select')
return click('.student_navigation .previous_object:first').then(() => {
const after = this.controller.get('selectedStudent')
checkSelection(after.id, '#student_select')
notEqual(before.id, after.id)
const previous = this.controller.get('students').indexOf(before) - 1
equal(previous, this.controller.get('students').indexOf(after))
})
})
// compares & checks before/after objects
test('clicking Previous Assignment button displays previous assignment', function () {
const before = this.controller.get('selectedAssignment')
checkSelection(before.id, '#assignment_select')
return click('.assignment_navigation .previous_object').then(() => {
const after = this.controller.get('selectedAssignment')
checkSelection(after.id, '#assignment_select')
notEqual(before.id, after.id)
const previous = this.controller.get('assignments').indexOf(before) - 1
equal(previous, this.controller.get('assignments').indexOf(after))
})
})
test('clicking previous then next will reset the focus for students', () =>
click('.student_navigation .previous_object:first').then(() =>
click('.student_navigation .next_object:first').then(() => {
equal($('.student_navigation .previous_object:first')[0], document.activeElement)
})
))
test('clicking previous then next will reset the focus for assignments', () =>
click('.assignment_navigation .previous_object').then(() =>
click('.assignment_navigation .next_object').then(() => {
equal($('.assignment_navigation .previous_object')[0], document.activeElement)
})
))
QUnit.module('screenreader_gradebook assignment navigation: display update', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return Ember.run(() => {
this.controller.set('selectedStudent', this.controller.get('students.firstObject'))
return this.controller.set(
'selectedAssignment',
this.controller.get('assignments.firstObject')
)
})
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('screenreader_gradebook assignment selection: grade for field updates', function () {
const assignment_name_selector = "label[for='student_and_assignment_grade']"
const selectedAssigName = this.controller.get('selectedAssignment.name')
const selectedStudentName = this.controller.get('selectedStudent.name')
checkText(assignment_name_selector, `Grade for ${selectedStudentName} - ${selectedAssigName}`)
Ember.run(() =>
this.controller.set('selectedAssignment', this.controller.get('assignments').objectAt(2))
)
const newSelectedAssigName = this.controller.get('selectedAssignment.name')
return checkText(
assignment_name_selector,
`Grade for ${selectedStudentName} - ${newSelectedAssigName}`
)
})
QUnit.module('screenreader_gradebook assignment navigation: assignment sorting', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(
() => (this.controller = App.__container__.lookup('controller:screenreader_gradebook'))
)
},
teardown() {
// resetting userSettings to default
Ember.run(() =>
this.controller.set(
'assignmentSort',
this.controller.get('assignmentSortOptions').findBy('value', 'assignment_group')
)
)
return Ember.run(App, 'destroy')
},
})
test('alphabetical', function () {
const before = this.controller.get('assignments.firstObject')
Ember.run(() =>
this.controller.set(
'assignmentSort',
this.controller.get('assignmentSortOptions').findBy('value', 'alpha')
)
)
buttonDisabled('.assignment_navigation .next_object', false)
buttonDisabled('.assignment_navigation .previous_object', true)
const first = this.controller.get('assignments.firstObject')
notEqual(before, first)
return click('.assignment_navigation .next_object').then(() =>
checkSelection(first.id, '#assignment_select')
)
})
test('due date', function () {
const before = this.controller.get('assignments.firstObject')
Ember.run(() =>
this.controller.set(
'assignmentSort',
this.controller.get('assignmentSortOptions').findBy('value', 'due_date')
)
)
buttonDisabled('.assignment_navigation .next_object', false)
buttonDisabled('.assignment_navigation .previous_object', true)
const first = this.controller.get('assignments.firstObject')
notEqual(before, first)
return click('.assignment_navigation .next_object').then(() =>
checkSelection(first.id, '#assignment_select')
)
})
test('changing sorting option with selectedAssignment', function () {
// SORT BY: alphabetical
Ember.run(() =>
this.controller.set(
'assignmentSort',
this.controller.get('assignmentSortOptions').findBy('value', 'alpha')
)
)
// check first assignment
return click('.assignment_navigation .next_object').then(() => {
const first = this.controller.get('selectedAssignment')
checkSelection(first.id, '#assignment_select')
equal(first.name, 'Apples are good')
const second = this.controller
.get('assignments')
.objectAt(this.controller.get('assignmentIndex') + 1)
// check Next
return click('.assignment_navigation .next_object').then(() => {
checkSelection(second.id, '#assignment_select')
notEqual(first.id, second.id)
equal(second.name, 'Big Bowl of Nachos')
// check Previous
return click('.assignment_navigation .previous_object').then(() => {
const selected = this.controller.get('selectedAssignment')
equal(selected.id, first.id)
checkSelection(selected.id, '#assignment_select')
const oldIndex = this.controller.get('assignmentIndex')
// SORT BY: due date
Ember.run(() =>
this.controller.set(
'assignmentSort',
this.controller.get('assignmentSortOptions').findBy('value', 'due_date')
)
)
// check selectedAssignment identity and index
equal(selected.id, this.controller.get('selectedAssignment.id'))
notEqual(oldIndex, this.controller.get('assignmentIndex'))
// check Next
const selectedIndex = this.controller.get('assignmentIndex')
const next = this.controller.get('assignments').objectAt(selectedIndex + 1)
return click('.assignment_navigation .next_object').then(() =>
checkSelection(next.id, '#assignment_select')
)
})
})
})
})
QUnit.module('screenreader_gradebook student navigation: section selection', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return Ember.run(() =>
this.controller.set('selectedSection', this.controller.get('sections.lastObject'))
)
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('prev/next still work', function () {
buttonDisabled('.student_navigation .previous_object:first', true)
buttonDisabled('.student_navigation .next_object:first', false)
// first in section
return click('.student_navigation .next_object:first').then(() => {
const first = this.controller.get('selectedStudent')
let index = this.controller.get('studentIndex')
buttonDisabled('.student_navigation .previous_object:first', true)
studentSectionAssertions(first, index, 0)
// second in section
// eslint-disable-next-line promise/catch-or-return
click('.student_navigation .next_object:first').then(() => {
const second = this.controller.get('selectedStudent')
index = this.controller.get('studentIndex')
buttonDisabled('.student_navigation .previous_object:first', false)
studentSectionAssertions(second, index, 1)
return notEqual(first, second)
})
return click('.student_navigation .previous_object:first').then(() => {
buttonDisabled('.student_navigation .previous_object:first', true)
return buttonDisabled('.student_navigation .next_object:first', false)
})
})
})
test('resets selectedStudent when student is not in both sections', function () {
return click('.student_navigation .next_object:first').then(() => {
const firstStudent = this.controller.get('selectedStudent')
Ember.run(() =>
this.controller.set('selectedSection', this.controller.get('sections.firstObject'))
)
const resetStudent = this.controller.get('selectedStudent')
notEqual(firstStudent, resetStudent)
equal(resetStudent, null)
return click('.student_navigation .next_object:first').then(() => {
const current = this.controller.get('selectedStudent')
notEqual(current, firstStudent)
return notEqual(current, resetStudent)
})
})
})
test('maintains selectedStudent when student is in both sections and updates navigation points', function () {
Ember.run(() =>
// requires a fixture for a student with enrollment in 2 sections
// and a previous/next option for all sections
this.controller.set('selectedStudent', this.controller.get('students').objectAt(4))
)
return visit('/').then(() => {
buttonDisabled('.student_navigation .previous_object:first', false)
buttonDisabled('.student_navigation .next_object:first', false)
const selected = this.controller.get('selectedStudent')
checkSelectedText(selected.sortable_name, '#student_select')
checkSelectedText(this.controller.get('selectedSection.name'), '#section_select')
// position in selected dropdown
let position = this.controller.get('studentsInSelectedSection').indexOf(selected)
equal(position, 1)
equal(this.controller.get('studentIndex'), position)
// change section
Ember.run(() =>
this.controller.set('selectedSection', this.controller.get('sections.firstObject'))
)
buttonDisabled('.student_navigation .previous_object:first', false)
buttonDisabled('.student_navigation .next_object:first', false)
const newSelected = this.controller.get('selectedStudent')
checkSelectedText(newSelected.sortable_name, '#student_select')
checkSelectedText(this.controller.get('selectedSection.name'), '#section_select')
equal(selected, newSelected)
// position in selected dropdown
position = this.controller.get('studentsInSelectedSection').indexOf(selected)
equal(position, 3)
equal(this.controller.get('studentIndex'), position)
})
})
QUnit.module(
'screenreader_gradebook student/assignment navigation: announcing selection with aria-live',
{
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return Ember.run(() => {
this.controller.set('selectedStudent', this.controller.get('students.firstObject'))
return this.controller.set(
'selectedAssignment',
this.controller.get('assignments.firstObject')
)
})
})
},
teardown() {
return Ember.run(App, 'destroy')
},
}
)
// unskip in EVAL-2505
QUnit.skip('aria-announcer', function () {
equal(Ember.$.trim(find('.aria-announcer').text()), '')
// eslint-disable-next-line promise/catch-or-return
click('.student_navigation .next_object:first').then(() => {
const expected = this.controller.get('selectedStudent.name')
equal(Ember.$.trim(find('.aria-announcer').text()), expected)
})
// eslint-disable-next-line promise/catch-or-return
click('.assignment_navigation .next_object').then(() => {
const expected = this.controller.get('selectedAssignment.name')
equal(Ember.$.trim(find('.aria-announcer').text()), expected)
})
return click('#hide_names_checkbox').then(() =>
Ember.run(() => equal(Ember.$.trim(find('.aria-announcer').text()), ''))
)
})

View File

@ -1,197 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import startApp from '../start_app'
import Ember from 'ember'
import fixtures from '../ajax_fixtures'
let App = null
const ariaMuted = 'Click to unmute.'
const ariaUnmuted = 'Click to mute.'
const dialogTitleMuted = 'Unmute Assignment'
const dialogTitleUnmuted = 'Mute Assignment'
const sendSuccess = (server, url, state) =>
server.respond('POST', url, [
200,
{'Content-Type': 'application/json'},
JSON.stringify({assignment: {muted: state}}),
])
const checkLabel = stateLabel =>
equal(find('#assignment_muted_check').attr('aria-label'), stateLabel)
const checkChecked = expectedBool =>
equal(find('#assignment_muted_check').prop('checked'), expectedBool)
function checkDialogClosed() {
const dialog = find('.ui-dialog:visible', 'body')
equal(dialog.length, 0, 'the dialog closes')
}
function closeDialog(dialog) {
click(find('.ui-dialog-titlebar-close', dialog))
return checkDialogClosed()
}
QUnit.module('screenreader_gradebook assignment_muter_component: muted', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.con = App.__container__.lookup('controller:screenreader_gradebook')
Ember.run(() =>
this.con.set(
'selectedAssignment',
Ember.copy(fixtures.assignment_groups[0].assignments[1], true)
)
)
return (this.server = sinon.fakeServer.create())
})
},
teardown() {
this.server.restore()
return Ember.run(App, 'destroy')
},
})
// unskip in FOO-4345
QUnit.skip('dialog cancels dialog without changes', () => {
checkLabel(ariaMuted)
checkChecked(true)
return click('#assignment_muted_check').then(() => {
const dialog = find('.ui-dialog:visible', 'body')
click('[data-action="cancel"]', dialog)
checkDialogClosed()
checkChecked(true)
return checkLabel(ariaMuted)
})
})
// unskip in FOO-4345
QUnit.skip('dialog opens and closes without changes', () => {
checkLabel(ariaMuted)
checkChecked(true)
return click('#assignment_muted_check').then(() => {
const dialog = find('.ui-dialog:visible', 'body')
equal(find('[data-action="unmute"]', dialog).text(), dialogTitleMuted)
closeDialog(dialog)
checkChecked(true)
return checkLabel(ariaMuted)
})
})
// unskip in FOO-4345
QUnit.skip('dialog opens and makes changes upon confirmation', function () {
const {server} = this
checkLabel(ariaMuted)
checkChecked(true)
return click('#assignment_muted_check').then(() => {
let dialog = find('.ui-dialog:visible', 'body')
click('[data-action="unmute"]', dialog)
sendSuccess(
server,
`${ENV.GRADEBOOK_OPTIONS.context_url}/assignments/${this.con.get(
'selectedAssignment.id'
)}/mute`,
false
)
return andThen(() => {
dialog = find('.ui-dialog:visible', 'body')
equal(dialog.length, 0, 'the dialog is closed')
checkChecked(false)
checkLabel(ariaUnmuted)
equal(this.con.get('selectedAssignment.muted'), false)
return server.restore()
})
})
})
QUnit.module('screenreader_gradebook assignment_muter_component: unmuted', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.con = App.__container__.lookup('controller:screenreader_gradebook')
Ember.run(() =>
this.con.set(
'selectedAssignment',
Ember.copy(fixtures.assignment_groups[0].assignments[0], true)
)
)
return (this.server = sinon.fakeServer.create())
})
},
teardown() {
this.server.restore()
return Ember.run(App, 'destroy')
},
})
// unskip in FOO-4345
QUnit.skip('dialog cancels dialog without changes', () => {
checkLabel(ariaUnmuted)
checkChecked(false)
return click('#assignment_muted_check').then(() => {
const dialog = find('.ui-dialog:visible', 'body')
click('[data-action="cancel"]', dialog)
checkDialogClosed()
checkChecked(false)
return checkLabel(ariaUnmuted)
})
})
// unskip in FOO-4345
QUnit.skip('dialog opens and closes without changes', () => {
checkLabel(ariaUnmuted)
checkChecked(false)
return click('#assignment_muted_check').then(() => {
const dialog = find('.ui-dialog:visible', 'body')
equal(find('[data-action="mute"]', dialog).text(), dialogTitleUnmuted)
closeDialog(dialog)
checkChecked(false)
return checkLabel(ariaUnmuted)
})
})
// unskip in FOO-4345
QUnit.skip('dialog opens and makes changes upon confirmation', function () {
const {server} = this
checkLabel(ariaUnmuted)
checkChecked(false)
return click('#assignment_muted_check').then(() => {
let dialog = find('.ui-dialog:visible', 'body')
click('[data-action="mute"]', dialog)
sendSuccess(
server,
`${ENV.GRADEBOOK_OPTIONS.context_url}/assignments/${this.con.get(
'selectedAssignment.id'
)}/mute`,
true
)
return andThen(() => {
dialog = find('.ui-dialog:visible', 'body')
equal(dialog.length, 0, 'the dialog is closed')
checkChecked(true)
checkLabel(ariaMuted)
equal(this.con.get('selectedAssignment.muted'), true)
return server.restore()
})
})
})

View File

@ -1,88 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import startApp from '../start_app'
import Ember from 'ember'
import fixtures from '../ajax_fixtures'
import $ from 'jquery'
import 'jquery-migrate'
import 'jquery-tinypubsub'
let App = null
QUnit.module('global settings', {
setup() {
fixtures.create()
App = startApp()
return visit('/').then(() => {
this.controller = App.__container__.lookup('controller:screenreader_gradebook')
return this.controller.set('hideStudentNames', false)
})
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('student names are hidden', () => {
const selection = '#student_select option[value=1]'
equal($(selection).text(), 'Barnes, Bob')
return click('#hide_names_checkbox').then(() => {
return click('#hide_names_checkbox').then(() => {
equal($(selection).text(), 'Barnes, Bob')
})
})
})
// unskip in EVAL-2505
QUnit.skip('secondary id says hidden', function () {
Ember.run(() => {
const student = this.controller.get('students.firstObject')
Ember.setProperties(student, {
isLoaded: true,
isLoading: false,
})
return this.controller.set('selectedStudent', student)
})
equal(Ember.$.trim(find('.secondary_id').text()), '')
click('#hide_names_checkbox')
return andThen(() => {
equal($.trim(find('.secondary_id:first').text()), 'hidden')
})
})
// unskip in EVAL-2505
QUnit.skip('view concluded enrollments', function () {
let enrollments = this.controller.get('enrollments')
ok(enrollments.content.length > 1)
enrollments.content.forEach(enrollment => ok(enrollment.workflow_state === undefined))
return click('#concluded_enrollments').then(() => {
enrollments = this.controller.get('enrollments')
equal(enrollments.content.length, 1)
const en = enrollments.objectAt(0)
ok(en.workflow_state === 'completed')
const completed_at = new Date(en.completed_at)
ok(completed_at.getTime() < new Date().getTime())
return click('#concluded_enrollments').then(() => {
enrollments = this.controller.get('enrollments')
return ok(enrollments.content.length > 1)
})
})
})

View File

@ -1,62 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import startApp from '../start_app'
import Ember from 'ember'
import fixtures from '../ajax_fixtures'
let App = null
const setSelection = selection => find('#arrange_assignments').val(selection)
const checkSelection = selection => equal(selection, find('#arrange_assignments').val())
QUnit.module('screenreader_gradebook assignment sorting: no saved setting', {
setup() {
fixtures.create()
App = startApp()
return visit('/')
},
teardown() {
return Ember.run(App, 'destroy')
},
})
test('defaults to assignment group', () => checkSelection('assignment_group'))
QUnit.module('screenreader_gradebook assignment sorting: toggle settings', {
setup() {
fixtures.create()
App = startApp()
return visit('/')
},
teardown() {
setSelection('assignment_group')
return Ember.run(App, 'destroy')
},
})
test('loads alphabetical sorting', () => {
setSelection('alpha')
visit('/')
checkSelection('alpha')
setSelection('due_date')
visit('/')
return checkSelection('due_date')
})
export default {}

View File

@ -1,67 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import userSettings from '@canvas/user-settings'
import startApp from '../start_app'
import Ember from 'ember'
import fixtures from '../ajax_fixtures'
let App = null
function setup(initialSetting) {
fixtures.create()
userSettings.contextSet('include_ungraded_assignments', initialSetting)
App = startApp()
return visit('/')
}
function runTest() {
const controller = App.__container__.lookup('controller:screenreader_gradebook')
const initial = controller.get('includeUngradedAssignments')
click('#ungraded')
return andThen(() => equal(!controller.get('includeUngradedAssignments'), initial))
}
QUnit.module('include ungraded assignments setting:false', {
setup() {
return setup.call(this, false)
},
teardown() {
return Ember.run(App, 'destroy')
},
})
// unskip in EVAL-2505
QUnit.skip('clicking the ungraded checkbox updates includeUngradedAssignments to true', () =>
runTest()
)
QUnit.module('include ungraded assignments setting:true', {
setup() {
return setup.call(this, true)
},
teardown() {
return Ember.run(App, 'destroy')
},
})
// unskip in EVAL-2505
QUnit.skip('clicking the ungraded checkbox updates includeUngradedAssignments to false', () =>
runTest()
)

View File

@ -1,93 +0,0 @@
//
// Copyright (C) 2018 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
//
import 'jquery'
import 'jquery-migrate'
import * as FinalGradeOverrideApi from '@canvas/grading/FinalGradeOverrideApi'
import ScreenreaderGradebookRoute from '../../routes/screenreader_gradebook_route'
QUnit.module('ScreenreaderGradebookRoute', suiteHooks => {
let server
suiteHooks.beforeEach(() => {
server = sinon.createFakeServer()
})
suiteHooks.afterEach(() => {
server.restore()
})
QUnit.module('model', contextHooks => {
let originalENV
let route
contextHooks.beforeEach(() => {
originalENV = window.ENV
window.ENV.GRADEBOOK_OPTIONS = {
custom_columns_url: 'custom_columns',
enrollments_url: 'enrollments',
final_grade_overrides_url: 'final_grade_overrides',
sections_url: 'sections',
outcome_links_url: 'outcome_links',
outcome_rollups_url: 'outcome_rollups',
}
route = ScreenreaderGradebookRoute.create()
})
contextHooks.afterEach(() => {
window.ENV = originalENV
})
QUnit.module('final_grade_overrides', hooks => {
hooks.beforeEach(() => {
window.ENV.GRADEBOOK_OPTIONS.final_grade_override_enabled = true
})
test('sets isLoaded to false while records are loading', () => {
const model = route.model()
strictEqual(model.final_grade_overrides.isLoaded, false)
})
test('sets isLoaded to true after records are loaded', async () => {
const apiStub = sinon
.stub(FinalGradeOverrideApi, 'getFinalGradeOverrides')
.returns(Promise.resolve({status: 200}))
const model = await route.model()
strictEqual(model.final_grade_overrides.isLoaded, true)
apiStub.restore()
})
test('sets content after records are loaded', async () => {
const overrideData = {
23: {
courseGrade: {
percentage: 91.1,
},
gradingPeriodGrades: {},
},
}
const apiStub = sinon
.stub(FinalGradeOverrideApi, 'getFinalGradeOverrides')
.returns(Promise.resolve({finalGradeOverrides: overrideData}))
const model = await route.model()
deepEqual(model.final_grade_overrides.content, {finalGradeOverrides: overrideData})
apiStub.restore()
})
})
})
})

View File

@ -1,65 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import ajax from 'ic-ajax'
import Ember from 'ember'
const clone = obj => Ember.copy(obj, true)
const data = {
attachment: {
file_state: '0',
workflow_state: 'to_be_zipped',
readable_size: '73 KB',
},
}
const numbers = [1, 2, 3]
export default {
create() {
window.ENV = {
submission_zip_url: '/courses/1/assignments/1/submissions?zip=1',
numbers_url: '/courses/1/numbers',
}
ajax.defineFixture(window.ENV.submission_zip_url, {
response: clone(data),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
return ajax.defineFixture(window.ENV.numbers_url, {
response: clone(numbers),
jqXHR: {
getResponseHeader() {
return {}
},
},
textStatus: 'success',
})
},
makeAvailable() {
data.attachment.file_state = 100
return (data.attachment.workflow_state = 'available')
},
}

View File

@ -1,46 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Application from '../index'
import Ember from 'ember'
export default function startApp() {
let App = null
// supresses this from logging during tests:
// DEBUG LOG: 'DEBUG: -------------------------------'
// DEBUG LOG: 'DEBUG: Ember : 1.4.0'
// DEBUG LOG: 'DEBUG: Handlebars : 1.3.0'
// DEBUG LOG: 'DEBUG: jQuery : 1.7.2'
// DEBUG LOG: 'DEBUG: -------------------------------'
Ember.LOG_VERSION = false
// since we don't plan on upgrading any of our ember code to new versions
// and we consider it "done" we don't care about deprecation warnings
Ember.TESTING_DEPRECATION = true
Ember.run.join(() => {
App = Application.create({
rootElement: '#fixtures',
})
App.Router.reopen({history: 'none'})
App.setupForTesting()
return App.injectTestHelpers()
})
window.App = App
return App
}

View File

@ -1,72 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import 'jquery'
import 'jquery-migrate'
import Ember from 'ember'
import fetchAllPages from '../../helpers/xhr/fetch_all_pages'
import fixtures from '../shared_ajax_fixtures'
const {ArrayProxy} = Ember
QUnit.module('Fetch all pages component', {
setup() {
// yes, this looks weird. if you run
// screenreader gradebook tests before this, it puts
// ember into test mode, and everything dies here when we
// try to do asynchronous work. This spec was originally written
// assuming that Ember was unmodified. This will not impact the
// screenreader gradebook tests, because they call "setupForTesting"
// in every setup.
Ember.testing = false
fixtures.create()
this.server = sinon.createFakeServer()
},
teardown() {
this.server.restore()
},
})
test('passes records through by default', assert => {
const start = assert.async()
return fetchAllPages(ENV.numbers_url).promise.then(records => {
start()
deepEqual(records.get('content'), [1, 2, 3])
})
})
test('populates existing array if provided', assert => {
const start = assert.async()
const myArray = ArrayProxy.create({content: []})
return fetchAllPages(ENV.numbers_url, {records: myArray}).promise.then(() => {
start()
deepEqual(myArray.get('content'), [1, 2, 3])
})
})
test('calls process if provided', assert => {
const start = assert.async()
return fetchAllPages(ENV.numbers_url, {
process(response) {
return response.map(x => x * 2)
},
}).promise.then(records => {
start()
deepEqual(records.get('content'), [2, 4, 6])
})
})

View File

@ -1,81 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import $ from 'jquery'
import Ember from 'ember'
import GradebookHeaderMenu from '../../jquery/GradebookHeaderMenu'
import SubmissionDetailsDialog from '../../jquery/SubmissionDetailsDialog'
const AssignmentsView = Ember.View.extend({
templateName: 'assignments',
mergeObjects(old_ag, new_ag) {
return Ember.setProperties(old_ag, new_ag)
},
actions: {
openDialog(dialogType) {
const con = this.controller
const assignment = con.get('selectedAssignment')
const options = {
assignment,
students: con.studentsThatCanSeeAssignment(assignment),
selected_section: __guard__(con.get('selectedSection'), x => x.id),
context_id: ENV.GRADEBOOK_OPTIONS.context_id,
context_url: ENV.GRADEBOOK_OPTIONS.context_url,
speed_grader_enabled: ENV.GRADEBOOK_OPTIONS.speed_grader_enabled,
change_grade_url: ENV.GRADEBOOK_OPTIONS.change_grade_url,
isAdmin: ENV.current_user_is_admin,
show_message_students_with_observers_dialog:
ENV.GRADEBOOK_OPTIONS.show_message_students_with_observers_dialog,
messageAttachmentUploadFolderId: ENV.GRADEBOOK_OPTIONS.message_attachment_upload_folder_id,
current_user_id: ENV.current_user.id,
}
const dialogs = {
assignment_details: GradebookHeaderMenu.prototype.showAssignmentDetails,
message_students: GradebookHeaderMenu.prototype.messageStudentsWho,
set_default_grade(opts) {
const onClose = () => $('#set_default_grade').focus()
return GradebookHeaderMenu.prototype.setDefaultGrade.call(this, {...opts, onClose})
},
curve_grades: GradebookHeaderMenu.prototype.curveGrades,
submission: SubmissionDetailsDialog.open,
}
switch (dialogType) {
case 'submission':
return dialogs[dialogType] != null
? dialogs[dialogType].call(
this,
con.get('selectedAssignment'),
con.get('selectedStudent'),
options
)
: undefined
default:
return dialogs[dialogType] != null ? dialogs[dialogType].call(this, options) : undefined
}
},
},
})
export default AssignmentsView
function __guard__(value, transform) {
return typeof value !== 'undefined' && value !== null ? transform(value) : undefined
}

View File

@ -1,24 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
const LearningMasteryView = Ember.View.extend({
templateName: 'learning_mastery',
})
export default LearningMasteryView

View File

@ -1,28 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import 'ic-tabs/dist/cjs/main'
const ScreenreaderGradebookView = Ember.View.extend({
didInsertElement() {
// horrible hack to get disabled instead of disabled="disabled" on buttons
return this.$('button:disabled').prop('disabled', true)
},
})
export default ScreenreaderGradebookView

View File

@ -1,86 +0,0 @@
//
// Copyright (C) 2014 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import Ember from 'ember'
import {useScope as useI18nScope} from '@canvas/i18n'
function capitalize(inputString) {
return inputString.charAt(0).toUpperCase() + inputString.substring(1).toLowerCase()
}
const I18n = useI18nScope('sr_gradebook')
const SelectionButtonsView = Ember.View.extend({
templateName: 'content_selection/selection_buttons',
list: null,
type: null,
selected: null,
classPath: function () {
return `${this.get('type')}_navigation`
}.property('type'),
previousLabel: function () {
const type = capitalize(this.get('type'))
return I18n.t('previous_object', 'Previous %{type}', {type})
}.property('type'),
nextLabel: function () {
const type = capitalize(this.get('type'))
return I18n.t('next_object', 'Next %{type}', {type})
}.property('type'),
disablePreviousButton: Ember.computed.lte('currentIndex', 0),
disableNextButton: function () {
const next = this.get('list').objectAt(this.get('currentIndex') + 1)
return !(this.get('list.length') && next)
}.property('currentIndex', 'list.@each'),
currentIndex: function () {
return this.get('list').indexOf(this.get('selected'))
}.property('selected', 'list.@each'),
actions: {
selectItem(goTo) {
const index = this.get('currentIndex')
const list = this.get('list')
let item = null
if (goTo === 'previous') {
item = list.objectAt(index - 1)
if (!list.objectAt(index - 2)) {
this.$('.next_object').focus()
}
}
if (goTo === 'next') {
item = list.objectAt(index + 1)
if (!list.objectAt(index + 2)) {
this.$('.previous_object').focus()
}
}
if (item) {
this.set('selected', item)
return this.get('controller').send('selectItem', this.get('type'), item)
}
},
},
})
export default SelectionButtonsView

View File

@ -1,46 +0,0 @@
/*
* Copyright (C) 2020 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import {useScope as useI18nScope} from '@canvas/i18n'
import GradebookConstants from './constants'
const I18n = useI18nScope('gradebookHelpers')
export default {
FLASH_ERROR_CLASS: '.ic-flash-error',
flashMaxLengthError() {
return $.flashError(
I18n.t('Note length cannot exceed %{maxLength} characters.', {
maxLength: GradebookConstants.MAX_NOTE_LENGTH,
})
)
},
maxLengthErrorShouldBeShown(textareaLength) {
return this.textareaIsGreaterThanMaxLength(textareaLength) && this.noErrorsOnPage()
},
noErrorsOnPage() {
return $.find(this.FLASH_ERROR_CLASS).length === 0
},
textareaIsGreaterThanMaxLength(textareaLength) {
return !this.textareaIsLessThanOrEqualToMaxLength(textareaLength)
},
textareaIsLessThanOrEqualToMaxLength(textareaLength) {
return textareaLength <= GradebookConstants.MAX_NOTE_LENGTH
},
}

View File

@ -1,36 +0,0 @@
/*
* Copyright (C) 2017 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React from 'react'
import ReactDOM from 'react-dom'
import GradebookMenu from '@canvas/gradebook-menu'
import App from './ember/index'
ReactDOM.render(
<GradebookMenu
courseUrl={ENV.GRADEBOOK_OPTIONS.context_url}
learningMasteryEnabled={Boolean(ENV.GRADEBOOK_OPTIONS.outcome_gradebook_enabled)}
enhancedIndividualGradebookEnabled={Boolean(
ENV.GRADEBOOK_OPTIONS.individual_gradebook_enhancements
)}
variant="IndividualGradebook"
/>,
document.querySelector('[data-component="GradebookSelector"]')
)
window.App = App.create()

View File

@ -1,109 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {useScope as useI18nScope} from '@canvas/i18n'
import $ from 'jquery'
import assignmentDetailsDialogTemplate from '../jst/AssignmentDetailsDialog.handlebars'
import round from '@canvas/round'
import 'jqueryui/dialog'
import '@canvas/util/jquery/fixDialogButtons'
const I18n = useI18nScope('assignment_details')
export default class AssignmentDetailsDialog {
static show(opts) {
const dialog = new AssignmentDetailsDialog(opts)
return dialog.show()
}
constructor({assignment, students}) {
this.compute = this.compute.bind(this)
this.assignment = assignment
this.students = students
}
show() {
const {locals} = this.compute()
const totalWidth = 100
const widthForValue = val => (totalWidth * val) / this.assignment.points_possible
$.extend(locals, {
showDistribution: locals.average && this.assignment.points_possible,
lowLeft: widthForValue(locals.min),
lqLeft: widthForValue(locals.lowerQuartile),
medianLeft: widthForValue(locals.median),
uqLeft: widthForValue(locals.upperQuartile),
highLeft: widthForValue(locals.max),
maxLeft: totalWidth,
highWidth: widthForValue(locals.max - locals.upperQuartile),
lowLqWidth: widthForValue(locals.lowerQuartile - locals.min),
medianLowWidth: widthForValue(locals.median - locals.lowerQuartile) + 1,
medianHighWidth: widthForValue(locals.upperQuartile - locals.median),
})
return $(assignmentDetailsDialogTemplate(locals)).dialog({
width: 500,
close() {
$(this).remove()
},
modal: true,
zIndex: 1000,
})
}
compute(opts = {students: this.students, assignment: this.assignment}) {
const {students, assignment} = opts
const scores = Object.values(students)
.filter(
student =>
student[`assignment_${assignment.id}`] &&
student[`assignment_${assignment.id}`].score != null
)
.map(student => student[`assignment_${assignment.id}`].score)
.sort()
const locals = {
assignment,
cnt: I18n.n(scores.length),
max: this.nonNumericGuard(Math.max(...scores)),
min: this.nonNumericGuard(Math.min(...scores)),
pointsPossible: this.nonNumericGuard(assignment.points_possible, I18n.t('N/A')),
average: this.nonNumericGuard(round(scores.reduce((a, b) => a + b, 0) / scores.length, 2)),
median: this.nonNumericGuard(this.percentile(scores, 0.5)),
lowerQuartile: this.nonNumericGuard(this.percentile(scores, 0.25)),
upperQuartile: this.nonNumericGuard(this.percentile(scores, 0.75)),
}
return {scores, locals}
}
nonNumericGuard(number, message = I18n.t('No graded submissions')) {
return Number.isFinite(number) && !Number.isNaN(number) ? I18n.n(number) : message
}
percentile(values, percentile) {
const k = Math.floor(percentile * (values.length - 1) + 1) - 1
const f = (percentile * (values.length - 1) + 1) % 1
return values[k] + f * (values[k + 1] - values[k])
}
}

View File

@ -1,156 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {useScope as useI18nScope} from '@canvas/i18n'
import $ from 'jquery'
import mute_dialog_template from '../jst/mute_dialog.handlebars'
import '@canvas/jquery/jquery.ajaxJSON'
import '@canvas/jquery/jquery.disableWhileLoading'
import 'jqueryui/dialog'
import 'jquery-tinypubsub'
const I18n = useI18nScope('assignment_muter')
export default class AssignmentMuter {
constructor($link, assignment, url, setter, options) {
;['confirmUnmute', 'afterUpdate', 'showDialog', 'updateLink'].forEach(
m => (this[m] = this[m].bind(this))
)
this.$link = $link
this.assignment = assignment
this.url = url
this.setter = setter
this.options = options
}
show(onClose) {
if (this.options && this.options.openDialogInstantly) {
if (this.assignment.muted) {
this.confirmUnmute()
} else {
this.showDialog(onClose)
}
} else {
this.$link = $(this.$link)
this.updateLink()
if (!this.options || this.options.canUnmute) {
this.$link.click(event => {
event.preventDefault()
if (this.assignment.muted) {
this.confirmUnmute()
} else {
this.showDialog(onClose)
}
})
}
}
}
updateLink() {
this.$link.text(this.assignment.muted ? I18n.t('Unmute Assignment') : I18n.t('Mute Assignment'))
}
showDialog(onClose) {
this.$dialog = $(mute_dialog_template()).dialog({
buttons: [
{
text: I18n.t('Cancel'),
class: 'Button',
'data-action': 'cancel',
click: () => this.$dialog.dialog('close'),
},
{
text: I18n.t('Mute Assignment'),
class: 'Button Button--primary',
'data-action': 'mute',
'data-text-while-loading': I18n.t('Muting Assignment...'),
click: () =>
this.$dialog.disableWhileLoading(
$.ajaxJSON(this.url, 'put', {status: true}, this.afterUpdate)
),
},
],
open: () =>
setTimeout(() => this.$dialog.parent().find('.ui-dialog-titlebar-close').focus(), 100),
close: () => this.$dialog.remove(),
resizable: false,
width: 400,
modal: true,
zIndex: 1000,
})
this.$dialog.on('dialogclose', onClose)
}
afterUpdate(serverResponse) {
const assignment = serverResponse.assignment
if (this.setter) {
this.setter(this.assignment, 'anonymize_students', assignment.anonymize_students)
this.setter(this.assignment, 'muted', assignment.muted)
} else {
this.assignment.anonymize_students = assignment.anonymize_students
this.assignment.muted = assignment.muted
}
if (!(this.options && this.options.openDialogInstantly)) this.updateLink()
this.$dialog.dialog('close')
$.publish('assignment_muting_toggled', [
{
...this.assignment,
anonymize_students: assignment.anonymize_students,
muted: assignment.muted,
},
])
}
confirmUnmute() {
this.$dialog = $('<div />')
.text(
I18n.t(
"This assignment is currently muted. That means students can't see their grades and feedback. Would you like to unmute now?"
)
)
.dialog({
buttons: [
{
text: I18n.t('Cancel'),
class: 'Button',
'data-action': 'cancel',
click: () => this.$dialog.dialog('close'),
},
{
text: I18n.t('unmute_button', 'Unmute Assignment'),
class: 'Button Button--primary',
'data-action': 'unmute',
'data-text-while-loading': I18n.t('unmuting_assignment', 'Unmuting Assignment...'),
click: () =>
this.$dialog.disableWhileLoading(
$.ajaxJSON(this.url, 'put', {status: false}, this.afterUpdate)
),
},
],
open: () =>
setTimeout(() => this.$dialog.parent().find('.ui-dialog-titlebar-close').focus(), 100),
close: () => this.$dialog.dialog('close'),
resizable: false,
title: I18n.t('unmute_assignment', 'Unmute Assignment'),
width: 400,
modal: true,
zIndex: 1000,
})
}
}

View File

@ -1,372 +0,0 @@
/*
* Copyright (C) 2020 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import {useScope as useI18nScope} from '@canvas/i18n'
import $ from 'jquery'
import messageStudents from '@canvas/message-students-dialog/jquery/message_students'
import AssignmentDetailsDialog from './AssignmentDetailsDialog'
import AssignmentMuter from './AssignmentMuter'
import * as FlashAlert from '@canvas/alerts/react/FlashAlert'
import SetDefaultGradeDialog from '@canvas/grading/jquery/SetDefaultGradeDialog'
import CurveGradesDialog from '@canvas/grading/jquery/CurveGradesDialog'
import gradebookHeaderMenuTemplate from '../jst/GradebookHeaderMenu.handlebars'
import re_upload_submissions_form from '@canvas/grading/jst/re_upload_submissions_form.handlebars'
import {map, filter} from 'lodash'
import authenticity_token from '@canvas/authenticity-token'
import MessageStudentsWhoHelper from '@canvas/grading/messageStudentsWhoHelper'
import React from 'react'
import ReactDOM from 'react-dom'
import MessageStudentsWithObserversDialog from '@canvas/message-students-dialog/react/MessageStudentsWhoDialog'
import {ApolloProvider} from 'react-apollo'
import {createClient} from '@canvas/apollo'
import '@canvas/jquery/jquery.instructure_forms'
import 'jqueryui/dialog'
import '@canvas/jquery/jquery.instructure_misc_helpers'
import '@canvas/jquery/jquery.instructure_misc_plugins'
import 'jquery-kyle-menu'
import replaceTags from '@canvas/util/replaceTags'
const I18n = useI18nScope('gradebookHeaderMenu')
const isAdmin = function () {
return ENV.current_user_is_admin
}
export default class GradebookHeaderMenu {
constructor(assignment1, $trigger, gradebook) {
this.showAssignmentDetails = this.showAssignmentDetails.bind(this)
this.messageStudentsWho = this.messageStudentsWho.bind(this)
this.setDefaultGrade = this.setDefaultGrade.bind(this)
this.curveGrades = this.curveGrades.bind(this)
this.downloadSubmissions = this.downloadSubmissions.bind(this)
this.reuploadSubmissions = this.reuploadSubmissions.bind(this)
this.canUnmute = this.canUnmute.bind(this)
this.assignment = assignment1
this.$trigger = $trigger
this.gradebook = gradebook
const templateLocals = {
assignmentUrl: `${this.gradebook.options.context_url}/assignments/${this.assignment.id}`,
speedGraderUrl: `${this.gradebook.options.context_url}/gradebook/speed_grader?assignment_id=${this.assignment.id}`,
}
if (!this.gradebook.options.speed_grader_enabled) {
templateLocals.speedGraderUrl = null
}
this.gradebook.allSubmissionsLoaded.done(() => {
// Reset the cache in case the user clicked on the menu while waiting for data
return (this.allSubmissionsLoaded = true)
})
this.$menu = $(gradebookHeaderMenuTemplate(templateLocals)).insertAfter(this.$trigger)
this.$trigger.kyleMenu({
noButton: true,
})
// need it to be a child of #gradebook_grid (not the header cell) to get over overflow:hidden obstacles.
this.$menu
.appendTo('#gradebook_grid')
.on('click', 'a', event => {
const action = this[$(event.target).data('action')]
if (action) {
action()
return false
}
})
.bind('popupopen popupclose', event => {
this.$trigger.toggleClass('ui-menu-trigger-menu-is-open', event.type === 'popupopen')
if (
event.type === 'popupclose' &&
event.originalEvent != null &&
event.originalEvent.type !== 'focusout'
) {
// defer because there seems to make sure this occurs after all of the jquery ui events
return setTimeout(() => {
return this.gradebook.grid.editActiveCell()
}, 0)
}
})
.bind('popupopen', () => {
return this.menuPopupOpenHandler(this.$menu)
})
.popup('open')
new AssignmentMuter(
this.$menu.find('[data-action=toggleMuting]'),
this.assignment,
`${this.gradebook.options.context_url}/assignments/${this.assignment.id}/mute`,
(a, _z, status) => {
a.muted = status
return this.gradebook.setAssignmentWarnings()
},
{
canUnmute: this.canUnmute(),
}
).show()
}
menuPopupOpenHandler(menu) {
// Hide any menu options that haven't had their dependencies met yet
this.hideMenuActionsWithUnmetDependencies(menu)
// Disable menu options if needed
return this.disableUnavailableMenuActions(menu)
}
hideMenuActionsWithUnmetDependencies(menu) {
let action, condition
const ref = {
showAssignmentDetails: this.allSubmissionsLoaded,
messageStudentsWho: this.allSubmissionsLoaded,
setDefaultGrade: this.allSubmissionsLoaded,
curveGrades:
this.allSubmissionsLoaded &&
this.assignment.grading_type !== 'pass_fail' &&
this.assignment.points_possible,
downloadSubmissions:
`${this.assignment.submission_types}`.match(
/(online_upload|online_text_entry|online_url)/
) && this.assignment.has_submitted_submissions,
reuploadSubmissions:
this.gradebook.options.gradebook_is_editable && this.assignment.submissions_downloads > 0,
}
const results = []
for (action in ref) {
condition = ref[action]
results.push(menu.find(`[data-action=${action}]`).showIf(condition))
}
return results
}
disableUnavailableMenuActions(menu) {
let actionToDisable, actionsToDisable, i, len, menuItem, ref
if (menu == null) {
return
}
actionsToDisable = []
if (((ref = this.assignment) != null ? ref.inClosedGradingPeriod : undefined) && !isAdmin()) {
actionsToDisable = ['curveGrades', 'setDefaultGrade']
}
if (!this.canUnmute()) {
actionsToDisable.push('toggleMuting')
}
const results = []
for (i = 0, len = actionsToDisable.length; i < len; i++) {
actionToDisable = actionsToDisable[i]
menuItem = menu.find(`[data-action=${actionToDisable}]`)
menuItem.addClass('ui-state-disabled')
results.push(menuItem.attr('aria-disabled', true))
}
return results
}
showAssignmentDetails(
opts = {
assignment: this.assignment,
students: this.gradebook.studentsThatCanSeeAssignment(
this.gradebook.students,
this.assignment
),
}
) {
const dialog = new AssignmentDetailsDialog(opts)
return dialog.show()
}
messageStudentsWho(
opts = {
assignment: this.assignment,
students: this.gradebook.studentsThatCanSeeAssignment(
this.gradebook.students,
this.assignment
),
messageAttachmentUploadFolderId: this.gradebook.options.message_attachment_upload_folder_id,
}
) {
let {students} = opts
const {assignment, messageAttachmentUploadFolderId} = opts
students = filter(students, student => {
return !student.isInactive && !student.isTestStudent
})
students = map(students, student => {
const sub = student[`assignment_${assignment.id}`]
return {
excused: sub.excused,
grade: sub.grade,
id: student.id,
latePolicyStatus: sub.late_policy_status,
name: student.name,
redoRequest: sub.redo_request,
score: sub != null ? sub.score : undefined,
// Both gradebooks share the Message Students dialog; prefer New Gradebook's casing
sortableName: student.sortable_name,
submittedAt: sub != null ? sub.submitted_at : undefined,
}
})
if (opts.show_message_students_with_observers_dialog) {
const mountPoint = document.querySelector(
"[data-component='MessageStudentsWithObserversModal']"
)
const props = {
assignment: {
allowedAttempts: assignment.allowed_attempts,
anonymizeStudents: assignment.anonymize_students,
courseId: assignment.course_id,
dueDate: assignment.due_at,
htmlUrl: assignment.html_url,
muted: assignment.muted,
pointsPossible: assignment.points_possible,
postManually: assignment.post_manually,
published: assignment.published,
gradingType: assignment.grading_type,
id: assignment.id,
name: assignment.name,
submissionTypes: assignment.submission_types,
},
onClose: () => {
ReactDOM.unmountComponentAtNode(mountPoint)
},
onSend: args => {
return MessageStudentsWhoHelper.sendMessageStudentsWho(
args.recipientsIds,
args.subject,
args.body,
`course_${assignment.course_id}`,
args.mediaFile,
args.attachmentIds
)
.then(FlashAlert.showFlashSuccess(I18n.t('Message sent successfully')))
.catch(FlashAlert.showFlashError(I18n.t('There was an error sending the message')))
},
messageAttachmentUploadFolderId,
students,
userId: opts.current_user_id,
}
ReactDOM.render(
<ApolloProvider client={createClient()}>
<MessageStudentsWithObserversDialog {...props} />
</ApolloProvider>,
mountPoint
)
} else {
const settings = MessageStudentsWhoHelper.settings(assignment, students)
return messageStudents(settings)
}
}
setDefaultGrade(
opts = {
assignment: this.assignment,
students: this.gradebook.studentsThatCanSeeAssignment(
this.gradebook.students,
this.assignment
),
context_id: this.gradebook.options.context_id,
selected_section: this.gradebook.sectionToShow,
}
) {
if (isAdmin() || !opts.assignment.inClosedGradingPeriod) {
return new SetDefaultGradeDialog(opts).show()
} else {
return $.flashError(
I18n.t(
'Unable to set default grade because this ' +
'assignment is due in a closed grading period for at least one student'
)
)
}
}
curveGrades(
opts = {
assignment: this.assignment,
students: this.gradebook.studentsThatCanSeeAssignment(
this.gradebook.students,
this.assignment
),
context_url: this.gradebook.options.context_url,
}
) {
let dialog
if (isAdmin() || !opts.assignment.inClosedGradingPeriod) {
dialog = new CurveGradesDialog(opts)
return dialog.show()
} else {
return $.flashError(
I18n.t(
'Unable to curve grades because this ' +
'assignment is due in a closed grading period for at least ' +
'one student'
)
)
}
}
downloadSubmissions() {
let base
const url = replaceTags(
this.gradebook.options.download_assignment_submissions_url,
'assignment_id',
this.assignment.id
)
INST.downloadSubmissions(url)
return (this.assignment.submissions_downloads =
((base = this.assignment).submissions_downloads != null
? base.submissions_downloads
: (base.submissions_downloads = 0)) + 1)
}
reuploadSubmissions() {
let locals
if (!this.$re_upload_submissions_form) {
locals = {
authenticityToken: authenticity_token(),
}
GradebookHeaderMenu.prototype.$re_upload_submissions_form = $(
re_upload_submissions_form(locals)
)
.dialog({
width: 400,
modal: true,
resizable: false,
autoOpen: false,
zIndex: 1000,
})
.submit(function () {
const data = $(this).getFormData()
if (!data.submissions_zip) {
return false
} else if (!data.submissions_zip.match(/\.zip$/)) {
$(this).formErrors({
submissions_zip: I18n.t('errors.upload_as_zip', 'Please upload files as a .zip'),
})
return false
}
})
}
const url = replaceTags(
this.gradebook.options.re_upload_submissions_url,
'assignment_id',
this.assignment.id
)
return this.$re_upload_submissions_form.attr('action', url).dialog('open')
}
canUnmute() {
return !(
this.assignment?.muted &&
this.assignment?.moderated_grading &&
!this.assignment?.grades_published
)
}
}

View File

@ -1,200 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import submissionDetailsDialog from '../jst/SubmissionDetailsDialog.handlebars'
import {useScope as useI18nScope} from '@canvas/i18n'
import GradeFormatHelper from '@canvas/grading/GradeFormatHelper'
import {originalityReportSubmissionKey} from '@canvas/grading/originalityReportHelper'
import {extractDataForTurnitin} from '@canvas/grading/Turnitin'
import OutlierScoreHelper from '@canvas/grading/OutlierScoreHelper'
import '../jst/_submission_detail.handlebars' // a partial needed by the SubmissionDetailsDialog template
import '@canvas/grading/jst/_turnitinScore.handlebars' // a partial needed by the submission_detail partial
import '@canvas/jquery/jquery.ajaxJSON'
import '@canvas/jquery/jquery.disableWhileLoading'
import '@canvas/jquery/jquery.instructure_forms'
import 'jqueryui/dialog'
import '@canvas/jquery/jquery.instructure_misc_plugins'
import 'jquery-scroll-to-visible/jquery.scrollTo'
import 'jquery-tinypubsub'
const I18n = useI18nScope('submission_details_dialog')
export default class SubmissionDetailsDialog {
constructor(assignment, student, options) {
this.assignment = assignment
this.student = student
this.options = options
const speedGraderUrl = this.options.speed_grader_enabled ? this.buildSpeedGraderUrl() : null
this.url = this.options.change_grade_url
.replace(':assignment', this.assignment.id)
.replace(':submission', this.student.id)
const submission = this.student[`assignment_${this.assignment.id}`]
this.submission = $.extend({}, submission, {
label: `student_grading_${this.assignment.id}`,
inputName: 'submission[posted_grade]',
assignment: this.assignment,
speedGraderUrl,
loading: true,
showPointsPossible:
(this.assignment.points_possible || this.assignment.points_possible === '0') &&
this.assignment.grading_type !== 'gpa_scale',
formattedPointsPossible: I18n.n(this.assignment.points_possible),
shouldShowExcusedOption: true,
isInPastGradingPeriodAndNotAdmin: submission.gradeLocked,
})
this.submission[`assignment_grading_type_is_${this.assignment.grading_type}`] = true
if (this.submission.excused) this.submission.grade = 'EX'
this.$el = $('<div class="use-css-transitions-for-show-hide" style="padding:0;"/>')
this.$el.html(submissionDetailsDialog(this.submission))
this.dialog = this.$el.dialog({
title: this.student.name,
width: 600,
resizable: false,
modal: true,
zIndex: 1000,
})
this.dialog.on('dialogclose', this.options.onClose)
this.dialog.on('dialogclose', () => {
this.dialog.dialog('destroy')
this.$el.remove()
})
this.dialog
.on('change', 'select[id="submission_to_view"]', event =>
this.dialog.find('.submission_detail').each(function (index) {
$(this).showIf(index === event.currentTarget.selectedIndex)
})
)
.on('submit', '.submission_details_grade_form', event => {
event.preventDefault()
let formData = $(event.currentTarget).getFormData()
const rawGrade = formData['submission[posted_grade]']
if (rawGrade.toUpperCase() === 'EX') {
formData = {'submission[excuse]': true}
} else {
formData['submission[posted_grade]'] = GradeFormatHelper.delocalizeGrade(rawGrade)
}
$(event.currentTarget.form).disableWhileLoading(
$.ajaxJSON(this.url, 'PUT', formData, data => {
this.update(data)
if (!data.excused) {
const outlierScoreHelper = new OutlierScoreHelper(
this.submission.score,
this.submission.assignment.points_possible
)
if (outlierScoreHelper.hasWarning()) {
$.flashWarning(outlierScoreHelper.warningMessage())
}
}
$.publish('submissions_updated', [this.submission.all_submissions])
setTimeout(() => this.dialog.dialog('close'), 500)
})
)
})
.on('submit', '.submission_details_add_comment_form', event => {
event.preventDefault()
$(event.currentTarget).disableWhileLoading(
$.ajaxJSON(this.url, 'PUT', $(event.currentTarget).getFormData(), data => {
this.update(data)
setTimeout(() => this.dialog.dialog('close'), 500)
})
)
})
const url = `${this.url}&include[]=submission_history&include[]=submission_comments&include[]=rubric_assessment`
const deferred = $.ajaxJSON(url, 'GET', {}, this.update)
this.dialog.find('.submission_details_comments').disableWhileLoading(deferred)
}
buildSpeedGraderUrl = () => {
const assignmentParam = `assignment_id=${this.assignment.id}`
const speedGraderUrlParams = this.assignment.anonymize_students
? assignmentParam
: `${assignmentParam}&student_id=${this.student.id}`
return encodeURI(`${this.options.context_url}/gradebook/speed_grader?${speedGraderUrlParams}`)
}
open = () => {
this.dialog.dialog('open')
this.scrollCommentsToBottom()
$('.ui-dialog-titlebar-close').focus()
}
scrollCommentsToBottom = () => this.dialog.find('.submission_details_comments').scrollTop(999999)
update = newData => {
$.extend(this.submission, newData)
this.submission.moreThanOneSubmission = this.submission.submission_history.length > 1
this.submission.loading = false
this.submission.submission_history.forEach(submission => {
submission.submission_comments &&
submission.submission_comments.forEach(comment => {
comment.url = `${this.options.context_url}/users/${comment.author_id}`
const urlPrefix = `${window.location.protocol}//${window.location.host}`
comment.image_url = `${urlPrefix}/images/users/${comment.author_id}`
})
submission.turnitin = extractDataForTurnitin(
submission,
originalityReportSubmissionKey(submission),
this.options.context_url
)
if (Object.keys(submission.turnitin).length === 0) {
submission.turnitin = extractDataForTurnitin(
submission,
`submission_${submission.id}`,
this.options.context_url
)
}
submission.attachments &&
submission.attachments.forEach(attachment => {
attachment.turnitin = extractDataForTurnitin(
submission,
`attachment_${attachment.id}`,
this.options.context_url
)
})
})
if (this.options.anonymous) {
this.submission.submission_comments.forEach(comment => {
if (comment.author.id !== ENV.current_user_id) {
comment.anonymous = comment.author.anonymous = true
comment.author_name = I18n.t('Student')
}
})
}
if (this.submission.excused) {
this.submission.grade = 'EX'
} else if (['points', 'percent'].includes(this.assignment.grading_type)) {
this.submission.grade = GradeFormatHelper.formatGrade(this.submission.grade)
}
this.dialog.html(submissionDetailsDialog(this.submission))
this.dialog.find('select').trigger('change')
return this.scrollCommentsToBottom()
}
static open(assignment, student, options) {
return new SubmissionDetailsDialog(assignment, student, options).open()
}
}

Some files were not shown because too many files have changed in this diff Show More