Version 2.1 #41
@ -26,14 +26,13 @@ type Category = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty category
|
/// An empty category
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = CategoryId.Empty
|
{ Id = CategoryId.Empty
|
||||||
WebLogId = WebLogId.Empty
|
WebLogId = WebLogId.Empty
|
||||||
Name = ""
|
Name = ""
|
||||||
Slug = ""
|
Slug = ""
|
||||||
Description = None
|
Description = None
|
||||||
ParentId = None
|
ParentId = None }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A comment on a post
|
/// A comment on a post
|
||||||
@ -68,8 +67,8 @@ type Comment = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty comment
|
/// An empty comment
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = CommentId.Empty
|
{ Id = CommentId.Empty
|
||||||
PostId = PostId.Empty
|
PostId = PostId.Empty
|
||||||
InReplyToId = None
|
InReplyToId = None
|
||||||
Name = ""
|
Name = ""
|
||||||
@ -77,8 +76,7 @@ type Comment = {
|
|||||||
Url = None
|
Url = None
|
||||||
Status = Pending
|
Status = Pending
|
||||||
PostedOn = Noda.epoch
|
PostedOn = Noda.epoch
|
||||||
Text = ""
|
Text = "" }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A page (text not associated with a date/time)
|
/// A page (text not associated with a date/time)
|
||||||
@ -125,8 +123,8 @@ type Page = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty page
|
/// An empty page
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = PageId.Empty
|
{ Id = PageId.Empty
|
||||||
WebLogId = WebLogId.Empty
|
WebLogId = WebLogId.Empty
|
||||||
AuthorId = WebLogUserId.Empty
|
AuthorId = WebLogUserId.Empty
|
||||||
Title = ""
|
Title = ""
|
||||||
@ -138,8 +136,7 @@ type Page = {
|
|||||||
Text = ""
|
Text = ""
|
||||||
Metadata = []
|
Metadata = []
|
||||||
PriorPermalinks = []
|
PriorPermalinks = []
|
||||||
Revisions = []
|
Revisions = [] }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A web log post
|
/// A web log post
|
||||||
@ -195,8 +192,8 @@ type Post = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty post
|
/// An empty post
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = PostId.Empty
|
{ Id = PostId.Empty
|
||||||
WebLogId = WebLogId.Empty
|
WebLogId = WebLogId.Empty
|
||||||
AuthorId = WebLogUserId.Empty
|
AuthorId = WebLogUserId.Empty
|
||||||
Status = Draft
|
Status = Draft
|
||||||
@ -211,8 +208,7 @@ type Post = {
|
|||||||
Episode = None
|
Episode = None
|
||||||
Metadata = []
|
Metadata = []
|
||||||
PriorPermalinks = []
|
PriorPermalinks = []
|
||||||
Revisions = []
|
Revisions = [] }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A mapping between a tag and its URL value, used to translate restricted characters (ex. "#1" -> "number-1")
|
/// A mapping between a tag and its URL value, used to translate restricted characters (ex. "#1" -> "number-1")
|
||||||
@ -232,12 +228,8 @@ type TagMap = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty tag mapping
|
/// An empty tag mapping
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = TagMapId.Empty
|
{ Id = TagMapId.Empty; WebLogId = WebLogId.Empty; Tag = ""; UrlValue = "" }
|
||||||
WebLogId = WebLogId.Empty
|
|
||||||
Tag = ""
|
|
||||||
UrlValue = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A theme
|
/// A theme
|
||||||
@ -257,12 +249,8 @@ type Theme = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty theme
|
/// An empty theme
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = ThemeId.Empty
|
{ Id = ThemeId.Empty; Name = ""; Version = ""; Templates = [] }
|
||||||
Name = ""
|
|
||||||
Version = ""
|
|
||||||
Templates = []
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A theme asset (a file served as part of a theme, at /themes/[theme]/[asset-path])
|
/// A theme asset (a file served as part of a theme, at /themes/[theme]/[asset-path])
|
||||||
@ -279,11 +267,8 @@ type ThemeAsset = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty theme asset
|
/// An empty theme asset
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = ThemeAssetId.Empty
|
{ Id = ThemeAssetId.Empty; UpdatedOn = Noda.epoch; Data = [||] }
|
||||||
UpdatedOn = Noda.epoch
|
|
||||||
Data = [||]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// An uploaded file
|
/// An uploaded file
|
||||||
@ -306,13 +291,8 @@ type Upload = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty upload
|
/// An empty upload
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = UploadId.Empty
|
{ Id = UploadId.Empty; WebLogId = WebLogId.Empty; Path = Permalink.Empty; UpdatedOn = Noda.epoch; Data = [||] }
|
||||||
WebLogId = WebLogId.Empty
|
|
||||||
Path = Permalink.Empty
|
|
||||||
UpdatedOn = Noda.epoch
|
|
||||||
Data = [||]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open Newtonsoft.Json
|
open Newtonsoft.Json
|
||||||
@ -361,8 +341,8 @@ type WebLog = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty web log
|
/// An empty web log
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = WebLogId.Empty
|
{ Id = WebLogId.Empty
|
||||||
Name = ""
|
Name = ""
|
||||||
Slug = ""
|
Slug = ""
|
||||||
Subtitle = None
|
Subtitle = None
|
||||||
@ -374,8 +354,7 @@ type WebLog = {
|
|||||||
Rss = RssOptions.Empty
|
Rss = RssOptions.Empty
|
||||||
AutoHtmx = false
|
AutoHtmx = false
|
||||||
Uploads = Database
|
Uploads = Database
|
||||||
RedirectRules = []
|
RedirectRules = [] }
|
||||||
}
|
|
||||||
|
|
||||||
/// Any extra path where this web log is hosted (blank if web log is hosted at the root of the domain)
|
/// Any extra path where this web log is hosted (blank if web log is hosted at the root of the domain)
|
||||||
[<JsonIgnore>]
|
[<JsonIgnore>]
|
||||||
@ -441,8 +420,8 @@ type WebLogUser = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty web log user
|
/// An empty web log user
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = WebLogUserId.Empty
|
{ Id = WebLogUserId.Empty
|
||||||
WebLogId = WebLogId.Empty
|
WebLogId = WebLogId.Empty
|
||||||
Email = ""
|
Email = ""
|
||||||
FirstName = ""
|
FirstName = ""
|
||||||
@ -452,8 +431,7 @@ type WebLogUser = {
|
|||||||
Url = None
|
Url = None
|
||||||
AccessLevel = Author
|
AccessLevel = Author
|
||||||
CreatedOn = Noda.epoch
|
CreatedOn = Noda.epoch
|
||||||
LastSeenOn = None
|
LastSeenOn = None }
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the user's displayed name
|
/// Get the user's displayed name
|
||||||
[<JsonIgnore>]
|
[<JsonIgnore>]
|
||||||
|
@ -256,8 +256,8 @@ type Episode = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty episode
|
/// An empty episode
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Media = ""
|
{ Media = ""
|
||||||
Length = 0L
|
Length = 0L
|
||||||
Duration = None
|
Duration = None
|
||||||
MediaType = None
|
MediaType = None
|
||||||
@ -275,8 +275,7 @@ type Episode = {
|
|||||||
SeasonNumber = None
|
SeasonNumber = None
|
||||||
SeasonDescription = None
|
SeasonDescription = None
|
||||||
EpisodeNumber = None
|
EpisodeNumber = None
|
||||||
EpisodeDescription = None
|
EpisodeDescription = None }
|
||||||
}
|
|
||||||
|
|
||||||
/// Format a duration for an episode
|
/// Format a duration for an episode
|
||||||
member this.FormatDuration() =
|
member this.FormatDuration() =
|
||||||
@ -460,11 +459,8 @@ type RedirectRule = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty redirect rule
|
/// An empty redirect rule
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
From = ""
|
{ From = ""; To = ""; IsRegex = false }
|
||||||
To = ""
|
|
||||||
IsRegex = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// An identifier for a custom feed
|
/// An identifier for a custom feed
|
||||||
@ -557,8 +553,8 @@ type PodcastOptions = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// A default set of podcast options
|
/// A default set of podcast options
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Title = ""
|
{ Title = ""
|
||||||
Subtitle = None
|
Subtitle = None
|
||||||
ItemsInFeed = 0
|
ItemsInFeed = 0
|
||||||
Summary = ""
|
Summary = ""
|
||||||
@ -573,8 +569,7 @@ type PodcastOptions = {
|
|||||||
PodcastGuid = None
|
PodcastGuid = None
|
||||||
FundingUrl = None
|
FundingUrl = None
|
||||||
FundingText = None
|
FundingText = None
|
||||||
Medium = None
|
Medium = None }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A custom feed
|
/// A custom feed
|
||||||
@ -594,12 +589,11 @@ type CustomFeed = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty custom feed
|
/// An empty custom feed
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
Id = CustomFeedId.Empty
|
{ Id = CustomFeedId.Empty
|
||||||
Source = Category CategoryId.Empty
|
Source = Category CategoryId.Empty
|
||||||
Path = Permalink.Empty
|
Path = Permalink.Empty
|
||||||
Podcast = None
|
Podcast = None }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Really Simple Syndication (RSS) options for this web log
|
/// Really Simple Syndication (RSS) options for this web log
|
||||||
@ -628,15 +622,14 @@ type RssOptions = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// An empty set of RSS options
|
/// An empty set of RSS options
|
||||||
static member Empty = {
|
static member Empty =
|
||||||
IsFeedEnabled = true
|
{ IsFeedEnabled = true
|
||||||
FeedName = "feed.xml"
|
FeedName = "feed.xml"
|
||||||
ItemsInFeed = None
|
ItemsInFeed = None
|
||||||
IsCategoryEnabled = true
|
IsCategoryEnabled = true
|
||||||
IsTagEnabled = true
|
IsTagEnabled = true
|
||||||
Copyright = None
|
Copyright = None
|
||||||
CustomFeeds = []
|
CustomFeeds = [] }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// An identifier for a tag mapping
|
/// An identifier for a tag mapping
|
||||||
|
@ -1146,8 +1146,8 @@ type SettingsModel = {
|
|||||||
} with
|
} with
|
||||||
|
|
||||||
/// Create a settings model from a web log
|
/// Create a settings model from a web log
|
||||||
static member FromWebLog(webLog: WebLog) = {
|
static member FromWebLog(webLog: WebLog) =
|
||||||
Name = webLog.Name
|
{ Name = webLog.Name
|
||||||
Slug = webLog.Slug
|
Slug = webLog.Slug
|
||||||
Subtitle = defaultArg webLog.Subtitle ""
|
Subtitle = defaultArg webLog.Subtitle ""
|
||||||
DefaultPage = webLog.DefaultPage
|
DefaultPage = webLog.DefaultPage
|
||||||
@ -1155,8 +1155,7 @@ type SettingsModel = {
|
|||||||
TimeZone = webLog.TimeZone
|
TimeZone = webLog.TimeZone
|
||||||
ThemeId = string webLog.ThemeId
|
ThemeId = string webLog.ThemeId
|
||||||
AutoHtmx = webLog.AutoHtmx
|
AutoHtmx = webLog.AutoHtmx
|
||||||
Uploads = string webLog.Uploads
|
Uploads = string webLog.Uploads }
|
||||||
}
|
|
||||||
|
|
||||||
/// Update a web log with settings from the form
|
/// Update a web log with settings from the form
|
||||||
member this.Update(webLog: WebLog) =
|
member this.Update(webLog: WebLog) =
|
||||||
|
@ -1074,6 +1074,147 @@ let manageRevisionsModelTests = testList "ManageRevisionsModel" [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the PostListItem type
|
||||||
|
let postListItemTests = testList "PostListItem" [
|
||||||
|
testList "FromPost" [
|
||||||
|
test "succeeds for a draft post" {
|
||||||
|
let post =
|
||||||
|
{ Post.Empty with
|
||||||
|
Id = PostId "draft-post"
|
||||||
|
AuthorId = WebLogUserId "myself"
|
||||||
|
Title = "Not Ready for Prime Time"
|
||||||
|
Permalink = Permalink "2021/draft.html"
|
||||||
|
UpdatedOn = Noda.epoch + Duration.FromHours 8
|
||||||
|
Text = "<h1>WIP</h1>" }
|
||||||
|
let model = PostListItem.FromPost { WebLog.Empty with TimeZone = "Etc/GMT-1" } post
|
||||||
|
Expect.equal model.Id "draft-post" "Id not filled properly"
|
||||||
|
Expect.equal model.AuthorId "myself" "AuthorId not filled properly"
|
||||||
|
Expect.equal model.Status "Draft" "Status not filled properly"
|
||||||
|
Expect.equal model.Title "Not Ready for Prime Time" "Title not filled properly"
|
||||||
|
Expect.equal model.Permalink "2021/draft.html" "Permalink not filled properly"
|
||||||
|
Expect.isFalse model.PublishedOn.HasValue "PublishedOn should not have had a value"
|
||||||
|
Expect.equal
|
||||||
|
model.UpdatedOn ((Noda.epoch + Duration.FromHours 9).ToDateTimeUtc()) "UpdatedOn not filled properly"
|
||||||
|
Expect.equal model.Text "<h1>WIP</h1>" "Text not filled properly"
|
||||||
|
Expect.isEmpty model.CategoryIds "There should have been no category IDs"
|
||||||
|
Expect.isEmpty model.Tags "There should have been no tags"
|
||||||
|
Expect.isNone model.Episode "There should not have been an episode"
|
||||||
|
Expect.isEmpty model.Metadata "There should have been no metadata"
|
||||||
|
}
|
||||||
|
test "succeeds for a published post in a non-root domain" {
|
||||||
|
let post =
|
||||||
|
{ Post.Empty with
|
||||||
|
Id = PostId "full-post"
|
||||||
|
AuthorId = WebLogUserId "me"
|
||||||
|
Status = Published
|
||||||
|
Title = "Finished Product"
|
||||||
|
Permalink = Permalink "2021/post.html"
|
||||||
|
PublishedOn = Some (Noda.epoch + Duration.FromHours 12)
|
||||||
|
UpdatedOn = Noda.epoch + Duration.FromHours 13
|
||||||
|
Text = """<a href="/other-post.html">Click</a>"""
|
||||||
|
CategoryIds = [ CategoryId "z"; CategoryId "y" ]
|
||||||
|
Tags = [ "test"; "unit" ]
|
||||||
|
Episode = Some { Episode.Empty with Media = "test.mp3" }
|
||||||
|
Metadata = [ { Name = "MyMeta"; Value = "MyValue" } ] }
|
||||||
|
let model =
|
||||||
|
PostListItem.FromPost { WebLog.Empty with UrlBase = "https://u.t/w"; TimeZone = "Etc/GMT+1" } post
|
||||||
|
Expect.equal model.Id "full-post" "Id not filled properly"
|
||||||
|
Expect.equal model.AuthorId "me" "AuthorId not filled properly"
|
||||||
|
Expect.equal model.Status "Published" "Status not filled properly"
|
||||||
|
Expect.equal model.Title "Finished Product" "Title not filled properly"
|
||||||
|
Expect.equal model.Permalink "2021/post.html" "Permalink not filled properly"
|
||||||
|
Expect.isTrue model.PublishedOn.HasValue "PublishedOn should not have had a value"
|
||||||
|
Expect.equal
|
||||||
|
model.PublishedOn.Value
|
||||||
|
((Noda.epoch + Duration.FromHours 11).ToDateTimeUtc())
|
||||||
|
"PublishedOn not filled properly"
|
||||||
|
Expect.equal
|
||||||
|
model.UpdatedOn ((Noda.epoch + Duration.FromHours 12).ToDateTimeUtc()) "UpdatedOn not filled properly"
|
||||||
|
Expect.equal model.Text """<a href="/w/other-post.html">Click</a>""" "Text not filled properly"
|
||||||
|
Expect.equal model.CategoryIds [ "z"; "y" ] "CategoryIds not filled properly"
|
||||||
|
Expect.equal model.Tags [ "test"; "unit" ] "Tags not filled properly"
|
||||||
|
Expect.isSome model.Episode "There should have been an episode"
|
||||||
|
Expect.equal model.Episode.Value.Media "test.mp3" "Episode not filled properly"
|
||||||
|
Expect.equal model.Metadata.Length 1 "There should have been 1 metadata item"
|
||||||
|
Expect.equal model.Metadata[0].Name "MyMeta" "Metadata not filled properly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the SettingModel type
|
||||||
|
let settingsModelTests = testList "SettingsModel" [
|
||||||
|
testList "FromWebLog" [
|
||||||
|
test "succeeds with no subtitle" {
|
||||||
|
let model =
|
||||||
|
SettingsModel.FromWebLog
|
||||||
|
{ WebLog.Empty with
|
||||||
|
Name = "The Web Log"
|
||||||
|
Slug = "the-web-log"
|
||||||
|
DefaultPage = "this-one"
|
||||||
|
PostsPerPage = 18
|
||||||
|
TimeZone = "America/Denver"
|
||||||
|
ThemeId = ThemeId "my-theme"
|
||||||
|
AutoHtmx = true }
|
||||||
|
Expect.equal model.Name "The Web Log" "Name not filled properly"
|
||||||
|
Expect.equal model.Slug "the-web-log" "Slug not filled properly"
|
||||||
|
Expect.equal model.Subtitle "" "Subtitle not filled properly"
|
||||||
|
Expect.equal model.DefaultPage "this-one" "DefaultPage not filled properly"
|
||||||
|
Expect.equal model.PostsPerPage 18 "PostsPerPage not filled properly"
|
||||||
|
Expect.equal model.TimeZone "America/Denver" "TimeZone not filled properly"
|
||||||
|
Expect.equal model.ThemeId "my-theme" "ThemeId not filled properly"
|
||||||
|
Expect.isTrue model.AutoHtmx "AutoHtmx should have been set"
|
||||||
|
Expect.equal model.Uploads "Database" "Uploads not filled properly"
|
||||||
|
}
|
||||||
|
test "succeeds with a subtitle" {
|
||||||
|
let model = SettingsModel.FromWebLog { WebLog.Empty with Subtitle = Some "sub here!" }
|
||||||
|
Expect.equal model.Subtitle "sub here!" "Subtitle not filled properly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "Update" [
|
||||||
|
test "succeeds with no subtitle" {
|
||||||
|
let webLog =
|
||||||
|
{ Name = "Interesting"
|
||||||
|
Slug = "some-stuff"
|
||||||
|
Subtitle = ""
|
||||||
|
DefaultPage = "that-one"
|
||||||
|
PostsPerPage = 8
|
||||||
|
TimeZone = "America/Chicago"
|
||||||
|
ThemeId = "test-theme"
|
||||||
|
AutoHtmx = true
|
||||||
|
Uploads = "Disk" }.Update WebLog.Empty
|
||||||
|
Expect.equal webLog.Name "Interesting" "Name not filled properly"
|
||||||
|
Expect.equal webLog.Slug "some-stuff" "Slug not filled properly"
|
||||||
|
Expect.isNone webLog.Subtitle "Subtitle should not have had a value"
|
||||||
|
Expect.equal webLog.DefaultPage "that-one" "DefaultPage not filled properly"
|
||||||
|
Expect.equal webLog.PostsPerPage 8 "PostsPerPage not filled properly"
|
||||||
|
Expect.equal webLog.TimeZone "America/Chicago" "TimeZone not filled properly"
|
||||||
|
Expect.equal webLog.ThemeId (ThemeId "test-theme") "ThemeId not filled properly"
|
||||||
|
Expect.isTrue webLog.AutoHtmx "AutoHtmx should have been set"
|
||||||
|
Expect.equal webLog.Uploads Disk "Uploads not filled properly"
|
||||||
|
}
|
||||||
|
test "succeeds with a subtitle" {
|
||||||
|
let webLog = { SettingsModel.FromWebLog WebLog.Empty with Subtitle = "Sub" }.Update WebLog.Empty
|
||||||
|
Expect.equal webLog.Subtitle (Some "Sub") "Subtitle should have had a value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Unit tests for the UserMessage type
|
||||||
|
let userMessageTests = testList "UserMessage" [
|
||||||
|
test "Success succeeds" {
|
||||||
|
Expect.equal UserMessage.Success.Level "success" "Level incorrect"
|
||||||
|
}
|
||||||
|
test "Info succeeds" {
|
||||||
|
Expect.equal UserMessage.Info.Level "primary" "Level incorrect"
|
||||||
|
}
|
||||||
|
test "Warning succeeds" {
|
||||||
|
Expect.equal UserMessage.Warning.Level "warning" "Level incorrect"
|
||||||
|
}
|
||||||
|
test "Error succeeds" {
|
||||||
|
Expect.equal UserMessage.Error.Level "danger" "Level incorrect"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
/// All tests in the Domain.ViewModels file
|
/// All tests in the Domain.ViewModels file
|
||||||
let all = testList "ViewModels" [
|
let all = testList "ViewModels" [
|
||||||
addBaseToRelativeUrlsTests
|
addBaseToRelativeUrlsTests
|
||||||
@ -1094,4 +1235,7 @@ let all = testList "ViewModels" [
|
|||||||
editUserModelTests
|
editUserModelTests
|
||||||
managePermalinksModelTests
|
managePermalinksModelTests
|
||||||
manageRevisionsModelTests
|
manageRevisionsModelTests
|
||||||
|
postListItemTests
|
||||||
|
settingsModelTests
|
||||||
|
userMessageTests
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user