Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ public void GetCourseStatisticsAtCentreForCategoryID_should_return_course_statis
CompletedCount = 5,
HideInLearnerPortal = false,
CategoryName = "Office 2007",
CourseTopic = "Microsoft Office",
LearningMinutes = "N/A"
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace DigitalLearningSolutions.Data.Tests.DataServices
{
using System.Collections.Generic;
using System.Linq;
using DigitalLearningSolutions.Data.DataServices;
using DigitalLearningSolutions.Data.Models.Common;
using DigitalLearningSolutions.Data.Tests.TestHelpers;
using FluentAssertions;
using NUnit.Framework;

public class CourseTopicsDataServiceTests
{
private CourseTopicsDataService courseTopicsDataService = null!;

[SetUp]
public void Setup()
{
var connection = ServiceTestHelper.GetDatabaseConnection();
courseTopicsDataService = new CourseTopicsDataService(connection);
}

[Test]
public void GetTopicsAvailableAtCentre_should_return_expected_items()
{
// Given
var expectedTopics = new List<Topic>
{
new Topic { CourseTopic = "Digital Skills", CourseTopicID = 2, Active = true},
new Topic { CourseTopic = "Excel", CourseTopicID = 5, Active = true},
new Topic { CourseTopic = "Microsoft Office", CourseTopicID = 3, Active = true},
new Topic { CourseTopic = "OneNote", CourseTopicID = 10, Active = true},
new Topic { CourseTopic = "Outlook", CourseTopicID = 7, Active = true},
new Topic { CourseTopic = "PowerPoint", CourseTopicID = 6, Active = true},
new Topic { CourseTopic = "SharePoint", CourseTopicID = 9, Active = true},
new Topic { CourseTopic = "Social Media", CourseTopicID = 8, Active = true},
new Topic { CourseTopic = "Undefined", CourseTopicID = 1, Active = true},
new Topic { CourseTopic = "Word", CourseTopicID = 4, Active = true}
};

// When
var result = courseTopicsDataService.GetCourseTopicsAvailableAtCentre(101).ToList();

// Then
result.Should().BeEquivalentTo(expectedTopics);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,13 @@ public IEnumerable<CourseStatistics> GetCourseStatisticsAtCentreForCategoryId(in
{AttemptsPassedQuery},
cu.HideInLearnerPortal,
cc.CategoryName,
ct.CourseTopic,
cu.LearningTimeMins AS LearningMinutes
FROM dbo.Customisations AS cu
INNER JOIN dbo.CentreApplications AS ca ON ca.ApplicationID = cu.ApplicationID
INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = ca.ApplicationID
INNER JOIN dbo.CourseCategories AS cc ON cc.CourseCategoryID = ap.CourseCategoryID
INNER JOIN dbo.CourseTopics AS ct ON ct.CourseTopicID = ap.CourseTopicId
WHERE (ap.CourseCategoryID = @categoryId OR @categoryId = 0)
AND (cu.CentreID = @centreId OR (cu.AllCentres = 1 AND ca.Active = 1))
AND ca.CentreID = @centreId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace DigitalLearningSolutions.Data.DataServices
{
using System.Collections.Generic;
using System.Data;
using Dapper;
using DigitalLearningSolutions.Data.Models.Common;

public interface ICourseTopicsDataService
{
IEnumerable<Topic> GetCourseTopicsAvailableAtCentre(int centreId);
}

public class CourseTopicsDataService : ICourseTopicsDataService
{
private readonly IDbConnection connection;

public CourseTopicsDataService(IDbConnection connection)
{
this.connection = connection;
}

public IEnumerable<Topic> GetCourseTopicsAvailableAtCentre(int centreId)
{
return connection.Query<Topic>(
@"SELECT CourseTopicID, CourseTopic, Active
FROM CourseTopics
WHERE (CentreID = @CentreID OR CentreID = 0) AND (Active = 1)
ORDER BY CourseTopic",
new { centreId }
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
using System;

public class CourseStatistics
public class CourseStatistics : BaseSearchableItem
{
public int CustomisationId { get; set; }
public int CentreId { get; set; }
Expand All @@ -17,11 +17,19 @@ public class CourseStatistics
public int AttemptsPassed { get; set; }
public bool HideInLearnerPortal { get; set; }
public string CategoryName { get; set; }
public string CourseTopic { get; set; }
public string LearningMinutes { get; set; }

public string CourseName => string.IsNullOrWhiteSpace(CustomisationName)
? ApplicationName
: ApplicationName + " - " + CustomisationName;

public double PassRate => AllAttempts == 0 ? 0 : Math.Round(100 * AttemptsPassed / (double)AllAttempts);

public override string SearchableName
{
get => SearchableNameOverrideForFuzzySharp ?? CourseName;
set => SearchableNameOverrideForFuzzySharp = value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public void Index_with_null_filterBy_and_new_filter_query_parameter_add_new_cook
public void Index_with_CLEAR_filterBy_and_new_filter_query_parameter_sets_new_cookie_value()
{
// Given
const string? filterBy = null;
const string? filterBy = "CLEAR";
const string? newFilterValue = "Role|IsCmsManager|true";

// When
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
namespace DigitalLearningSolutions.Web.Tests.Controllers.TrackingSystem.CourseSetup
{
using System.Collections.Generic;
using DigitalLearningSolutions.Data.DataServices;
using DigitalLearningSolutions.Data.Models.Common;
using DigitalLearningSolutions.Data.Models.Courses;
using DigitalLearningSolutions.Data.Services;
using DigitalLearningSolutions.Web.Controllers.TrackingSystem.CourseSetup;
using DigitalLearningSolutions.Web.Tests.ControllerHelpers;
using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.CourseSetup;
using FakeItEasy;
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NUnit.Framework;

public class CourseSetupControllerTests
{
private readonly List<Category> categories = new List<Category>
{
new Category { CategoryName = "Category 1" },
new Category { CategoryName = "Category 2" }
};

private readonly List<CourseStatistics> courses = new List<CourseStatistics>
{
new CourseStatistics
{
ApplicationName = "Course",
CustomisationName = "Customisation",
Active = true,
CourseTopic = "Topic 1",
CategoryName = "Category 1",
HideInLearnerPortal = true,
DelegateCount = 1,
CompletedCount = 1
}
};

private readonly List<Topic> topics = new List<Topic>
{
new Topic { CourseTopic = "Topic 1" },
new Topic { CourseTopic = "Topic 2" }
};

private CourseSetupController controller = null!;
private ICourseCategoriesDataService courseCategoryDataService = null!;
private ICourseService courseService = null!;
private ICourseTopicsDataService courseTopicsDataService = null!;
private HttpRequest httpRequest = null!;
private HttpResponse httpResponse = null!;

[SetUp]
public void Setup()
{
courseCategoryDataService = A.Fake<ICourseCategoriesDataService>();
courseTopicsDataService = A.Fake<ICourseTopicsDataService>();
courseService = A.Fake<ICourseService>();

A.CallTo(() => courseService.GetCentreSpecificCourseStatistics(A<int>._, A<int>._)).Returns(courses);
A.CallTo(() => courseCategoryDataService.GetCategoriesForCentreAndCentrallyManagedCourses(A<int>._))
.Returns(categories);
A.CallTo(() => courseTopicsDataService.GetCourseTopicsAvailableAtCentre(A<int>._)).Returns(topics);

httpRequest = A.Fake<HttpRequest>();
httpResponse = A.Fake<HttpResponse>();
const string cookieName = "CourseFilter";
const string cookieValue = "Status|Active|false";

controller = new CourseSetupController(courseService, courseCategoryDataService, courseTopicsDataService)
.WithMockHttpContextWithCookie(httpRequest, cookieName, cookieValue, httpResponse)
.WithMockUser(true)
.WithMockTempData();
}

[Test]
public void Index_with_no_query_parameters_uses_cookie_value_for_filterBy()
{
// When
var result = controller.Index();

// Then
result.As<ViewResult>().Model.As<CourseSetupViewModel>().FilterBy.Should()
.Be("Status|Active|false");
}

[Test]
public void Index_with_query_parameters_uses_query_parameter_value_for_filterBy()
{
// Given
const string filterBy = "Status|HideInLearnerPortal|true";
A.CallTo(() => httpRequest.Query.ContainsKey("filterBy")).Returns(true);

// When
var result = controller.Index(filterBy: filterBy);

// Then
result.As<ViewResult>().Model.As<CourseSetupViewModel>().FilterBy.Should()
.Be(filterBy);
}

[Test]
public void Index_with_CLEAR_filterBy_query_parameter_removes_cookie()
{
// Given
const string filterBy = "CLEAR";

// When
var result = controller.Index(filterBy: filterBy);

// Then
A.CallTo(() => httpResponse.Cookies.Delete("CourseFilter")).MustHaveHappened();
result.As<ViewResult>().Model.As<CourseSetupViewModel>().FilterBy.Should()
.BeNull();
}

[Test]
public void Index_with_null_filterBy_and_new_filter_query_parameter_adds_new_cookie_value()
{
// Given
const string? filterBy = null;
const string newFilterValue = "Status|HideInLearnerPortal|true";
A.CallTo(() => httpRequest.Query.ContainsKey("filterBy")).Returns(true);

// When
var result = controller.Index(filterBy: filterBy, filterValue: newFilterValue);

// Then
A.CallTo(() => httpResponse.Cookies.Append("CourseFilter", newFilterValue, A<CookieOptions>._))
.MustHaveHappened();
result.As<ViewResult>().Model.As<CourseSetupViewModel>().FilterBy.Should()
.Be(newFilterValue);
}

[Test]
public void Index_with_CLEAR_filterBy_and_new_filter_query_parameter_sets_new_cookie_value()
{
// Given
const string filterBy = "CLEAR";
const string newFilterValue = "Status|HideInLearnerPortal|true";

// When
var result = controller.Index(filterBy: filterBy, filterValue: newFilterValue);

// Then
A.CallTo(() => httpResponse.Cookies.Append("CourseFilter", newFilterValue, A<CookieOptions>._))
.MustHaveHappened();
result.As<ViewResult>().Model.As<CourseSetupViewModel>().FilterBy.Should()
.Be(newFilterValue);
}

[Test]
public void Index_with_no_filtering_should_default_to_Active_courses()
{
// Given
var controllerWithNoCookies = new CourseSetupController(
courseService,
courseCategoryDataService,
courseTopicsDataService
)
.WithDefaultContext()
.WithMockUser(true);

// When
var result = controllerWithNoCookies.Index();

// Then
result.As<ViewResult>().Model.As<CourseSetupViewModel>().FilterBy.Should()
.Be("Status|Active|true");
}
}
}
Loading