Merge remote-tracking branch 'origin/v11/dev' into v12/dev
This commit is contained in:
@@ -8,7 +8,7 @@ namespace Umbraco.Cms.Core.Configuration.Models;
|
|||||||
public class RichTextEditorSettings
|
public class RichTextEditorSettings
|
||||||
{
|
{
|
||||||
internal const string StaticValidElements =
|
internal const string StaticValidElements =
|
||||||
"+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,video[*],audio[*],picture[*],source[*],canvas[*]";
|
"+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]";
|
||||||
|
|
||||||
internal const string StaticInvalidElements = "font";
|
internal const string StaticInvalidElements = "font";
|
||||||
|
|
||||||
|
|||||||
@@ -15,4 +15,43 @@
|
|||||||
<key alias="translation">Traduceri</key>
|
<key alias="translation">Traduceri</key>
|
||||||
<key alias="users">Utilizatori</key>
|
<key alias="users">Utilizatori</key>
|
||||||
</area>
|
</area>
|
||||||
|
<area alias="treeHeaders">
|
||||||
|
<key alias="content">Conţinut</key>
|
||||||
|
<key alias="contentBlueprints">Șabloane de conținut</key>
|
||||||
|
<key alias="media">Media</key>
|
||||||
|
<key alias="cacheBrowser">Cache Browser</key>
|
||||||
|
<key alias="contentRecycleBin">Cos de gunoi</key>
|
||||||
|
<key alias="createdPackages">Pachete create</key>
|
||||||
|
<key alias="dataTypes">Tipuri de date</key>
|
||||||
|
<key alias="dictionary">Dicţionar</key>
|
||||||
|
<key alias="installedPackages">Pachete instalate</key>
|
||||||
|
<key alias="installSkin">Instalare skin</key>
|
||||||
|
<key alias="installStarterKit">Instalați trusa de pornire</key>
|
||||||
|
<key alias="languages">Limbi</key>
|
||||||
|
<key alias="localPackage">Instalați pachetul local</key>
|
||||||
|
<key alias="macros">Macros</key>
|
||||||
|
<key alias="mediaTypes">Tipuri media</key>
|
||||||
|
<key alias="member">Membrii</key>
|
||||||
|
<key alias="memberGroups">Grupuri de membri</key>
|
||||||
|
<key alias="memberRoles">Rolurile membrilor</key>
|
||||||
|
<key alias="memberTypes">Tipuri de membri</key>
|
||||||
|
<key alias="documentTypes">Tipuri de documente</key>
|
||||||
|
<key alias="relationTypes">Tipuri de relații</key>
|
||||||
|
<key alias="packager">Pachete</key>
|
||||||
|
<key alias="packages">Pachete</key>
|
||||||
|
<key alias="partialViews">Vizualizări parțiale</key>
|
||||||
|
<key alias="partialViewMacros">Vizualizare parțială fișiere macro</key>
|
||||||
|
<key alias="repositories">Instalați din depozit</key>
|
||||||
|
<key alias="runway">Instalați pista</key>
|
||||||
|
<key alias="runwayModules">Module piste</key>
|
||||||
|
<key alias="scripting">Fișiere de scriptare</key>
|
||||||
|
<key alias="scripts">Scripturi</key>
|
||||||
|
<key alias="stylesheets">Stylesheets</key>
|
||||||
|
<key alias="templates">Șabloane</key>
|
||||||
|
<key alias="logViewer">Vizualizator de jurnal</key>
|
||||||
|
<key alias="users">Utilizatori</key>
|
||||||
|
<key alias="settingsGroup">Setări</key>
|
||||||
|
<key alias="templatingGroup">Modelare</key>
|
||||||
|
<key alias="thirdPartyGroup">Terț</key>
|
||||||
|
</area>
|
||||||
</language>
|
</language>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) Umbraco.
|
// Copyright (c) Umbraco.
|
||||||
// See LICENSE for more details.
|
// See LICENSE for more details.
|
||||||
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -125,6 +125,7 @@ public abstract class ConfigurationEditor<TConfiguration> : ConfigurationEditor
|
|||||||
PropertyType = property.PropertyType,
|
PropertyType = property.PropertyType,
|
||||||
Description = attribute.Description,
|
Description = attribute.Description,
|
||||||
HideLabel = attribute.HideLabel,
|
HideLabel = attribute.HideLabel,
|
||||||
|
SortOrder = attribute.SortOrder,
|
||||||
View = attributeView,
|
View = attributeView,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,6 +151,8 @@ public abstract class ConfigurationEditor<TConfiguration> : ConfigurationEditor
|
|||||||
field.PropertyName = property.Name;
|
field.PropertyName = property.Name;
|
||||||
field.PropertyType = property.PropertyType;
|
field.PropertyType = property.PropertyType;
|
||||||
|
|
||||||
|
field.SortOrder = attribute.SortOrder;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(attribute.Key))
|
if (!string.IsNullOrWhiteSpace(attribute.Key))
|
||||||
{
|
{
|
||||||
field.Key = attribute.Key;
|
field.Key = attribute.Key;
|
||||||
@@ -182,6 +185,6 @@ public abstract class ConfigurationEditor<TConfiguration> : ConfigurationEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields;
|
return fields.OrderBy(x => x.SortOrder).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||||
using Umbraco.Extensions;
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||||
@@ -47,6 +48,7 @@ public class ConfigurationField
|
|||||||
HideLabel = attribute.HideLabel;
|
HideLabel = attribute.HideLabel;
|
||||||
Key = attribute.Key;
|
Key = attribute.Key;
|
||||||
View = attribute.View;
|
View = attribute.View;
|
||||||
|
SortOrder = attribute.SortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -77,6 +79,12 @@ public class ConfigurationField
|
|||||||
[DataMember(Name = "description")]
|
[DataMember(Name = "description")]
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the sort order of the field.
|
||||||
|
/// </summary>
|
||||||
|
[DataMember(Name = "sortOrder")]
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether to hide the label of the field.
|
/// Gets or sets a value indicating whether to hide the label of the field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -124,6 +124,11 @@ public class ConfigurationFieldAttribute : Attribute
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string? View { get; }
|
public string? View { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the sort order to use to render the field editor.
|
||||||
|
/// </summary>
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the description of the field.
|
/// Gets or sets the description of the field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1146,6 +1146,8 @@ public class ContentService : RepositoryService, IContentService
|
|||||||
|
|
||||||
var allLangs = _languageRepository.GetMany().ToList();
|
var allLangs = _languageRepository.GetMany().ToList();
|
||||||
|
|
||||||
|
// Change state to publishing
|
||||||
|
content.PublishedState = PublishedState.Publishing;
|
||||||
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
||||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ internal class TagRepository : EntityRepositoryBase<int, ITag>, ITagRepository
|
|||||||
var sql1 = $@"INSERT INTO cmsTags (tag, {group}, languageId)
|
var sql1 = $@"INSERT INTO cmsTags (tag, {group}, languageId)
|
||||||
SELECT tagSet.tag, tagSet.{group}, tagSet.languageId
|
SELECT tagSet.tag, tagSet.{group}, tagSet.languageId
|
||||||
FROM {tagSetSql}
|
FROM {tagSetSql}
|
||||||
LEFT OUTER JOIN cmsTags ON (tagSet.tag = cmsTags.tag AND tagSet.{group} = cmsTags.{group} AND COALESCE(tagSet.languageId, -1) = COALESCE(cmsTags.languageId, -1))
|
LEFT OUTER JOIN cmsTags ON (tagSet.tag LIKE cmsTags.tag AND tagSet.{group} = cmsTags.{group} AND COALESCE(tagSet.languageId, -1) = COALESCE(cmsTags.languageId, -1))
|
||||||
WHERE cmsTags.id IS NULL";
|
WHERE cmsTags.id IS NULL";
|
||||||
|
|
||||||
Database.Execute(sql1);
|
Database.Execute(sql1);
|
||||||
@@ -321,7 +321,7 @@ WHERE r.tagId IS NULL";
|
|||||||
Sql<ISqlContext> sql = GetTaggedEntitiesSql(objectType, culture);
|
Sql<ISqlContext> sql = GetTaggedEntitiesSql(objectType, culture);
|
||||||
|
|
||||||
sql = sql
|
sql = sql
|
||||||
.Where<TagDto>(dto => dto.Text == tag);
|
.WhereLike<TagDto>(dto => dto.Text, tag);
|
||||||
|
|
||||||
if (group.IsNullOrWhiteSpace() == false)
|
if (group.IsNullOrWhiteSpace() == false)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -941,15 +941,13 @@
|
|||||||
const openPreviewWindow = () => {
|
const openPreviewWindow = () => {
|
||||||
// Chromes popup blocker will kick in if a window is opened
|
// Chromes popup blocker will kick in if a window is opened
|
||||||
// without the initial scoped request. This trick will fix that.
|
// without the initial scoped request. This trick will fix that.
|
||||||
//
|
|
||||||
const previewWindow = $window.open('preview/?init=true', 'umbpreview');
|
const previewWindow = $window.open(`preview/?id=${content.id}${$scope.culture ? `&culture=${$scope.culture}` : ''}`, 'umbpreview');
|
||||||
|
|
||||||
|
previewWindow.addEventListener('load', () => {
|
||||||
|
previewWindow.location.href = previewWindow.document.URL;
|
||||||
|
});
|
||||||
|
|
||||||
// Build the correct path so both /#/ and #/ work.
|
|
||||||
let query = 'id=' + content.id;
|
|
||||||
if ($scope.culture) {
|
|
||||||
query += "#?culture=" + $scope.culture;
|
|
||||||
}
|
|
||||||
previewWindow.location.href = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?' + query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//The user cannot save if they don't have access to do that, in which case we just want to preview
|
//The user cannot save if they don't have access to do that, in which case we just want to preview
|
||||||
|
|||||||
@@ -2,324 +2,329 @@
|
|||||||
// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php
|
// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php
|
||||||
|
|
||||||
function keyboardService($window, $timeout) {
|
function keyboardService($window, $timeout) {
|
||||||
|
|
||||||
var keyboardManagerService = {};
|
|
||||||
|
|
||||||
var defaultOpt = {
|
|
||||||
'type': 'keydown',
|
|
||||||
'propagate': false,
|
|
||||||
'inputDisabled': false,
|
|
||||||
'target': $window.document,
|
|
||||||
'keyCode': false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
|
var keyboardManagerService = {};
|
||||||
var shift_nums = {
|
|
||||||
"`": "~",
|
|
||||||
"1": "!",
|
|
||||||
"2": "@",
|
|
||||||
"3": "#",
|
|
||||||
"4": "$",
|
|
||||||
"5": "%",
|
|
||||||
"6": "^",
|
|
||||||
"7": "&",
|
|
||||||
"8": "*",
|
|
||||||
"9": "(",
|
|
||||||
"0": ")",
|
|
||||||
"-": "_",
|
|
||||||
"=": "+",
|
|
||||||
";": ":",
|
|
||||||
"'": "\"",
|
|
||||||
",": "<",
|
|
||||||
".": ">",
|
|
||||||
"/": "?",
|
|
||||||
"\\": "|"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Special Keys - and their codes
|
var defaultOpt = {
|
||||||
var special_keys = {
|
'type': 'keydown',
|
||||||
'esc': 27,
|
'propagate': false,
|
||||||
'escape': 27,
|
'inputDisabled': false,
|
||||||
'tab': 9,
|
'target': $window.document,
|
||||||
'space': 32,
|
'keyCode': false
|
||||||
'return': 13,
|
};
|
||||||
'enter': 13,
|
|
||||||
'backspace': 8,
|
|
||||||
|
|
||||||
'scrolllock': 145,
|
// Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
|
||||||
'scroll_lock': 145,
|
var shift_nums = {
|
||||||
'scroll': 145,
|
"`": "~",
|
||||||
'capslock': 20,
|
"1": "!",
|
||||||
'caps_lock': 20,
|
"2": "@",
|
||||||
'caps': 20,
|
"3": "#",
|
||||||
'numlock': 144,
|
"4": "$",
|
||||||
'num_lock': 144,
|
"5": "%",
|
||||||
'num': 144,
|
"6": "^",
|
||||||
|
"7": "&",
|
||||||
|
"8": "*",
|
||||||
|
"9": "(",
|
||||||
|
"0": ")",
|
||||||
|
"-": "_",
|
||||||
|
"=": "+",
|
||||||
|
";": ":",
|
||||||
|
"'": "\"",
|
||||||
|
",": "<",
|
||||||
|
".": ">",
|
||||||
|
"/": "?",
|
||||||
|
"\\": "|"
|
||||||
|
};
|
||||||
|
|
||||||
'pause': 19,
|
// Special Keys - and their codes
|
||||||
'break': 19,
|
var special_keys = {
|
||||||
|
'esc': 27,
|
||||||
|
'escape': 27,
|
||||||
|
'tab': 9,
|
||||||
|
'space': 32,
|
||||||
|
'return': 13,
|
||||||
|
'enter': 13,
|
||||||
|
'backspace': 8,
|
||||||
|
|
||||||
'insert': 45,
|
'scrolllock': 145,
|
||||||
'home': 36,
|
'scroll_lock': 145,
|
||||||
'delete': 46,
|
'scroll': 145,
|
||||||
'end': 35,
|
'capslock': 20,
|
||||||
|
'caps_lock': 20,
|
||||||
|
'caps': 20,
|
||||||
|
'numlock': 144,
|
||||||
|
'num_lock': 144,
|
||||||
|
'num': 144,
|
||||||
|
|
||||||
'pageup': 33,
|
'pause': 19,
|
||||||
'page_up': 33,
|
'break': 19,
|
||||||
'pu': 33,
|
|
||||||
|
|
||||||
'pagedown': 34,
|
'insert': 45,
|
||||||
'page_down': 34,
|
'home': 36,
|
||||||
'pd': 34,
|
'delete': 46,
|
||||||
|
'end': 35,
|
||||||
|
|
||||||
'left': 37,
|
'pageup': 33,
|
||||||
'up': 38,
|
'page_up': 33,
|
||||||
'right': 39,
|
'pu': 33,
|
||||||
'down': 40,
|
|
||||||
|
|
||||||
'f1': 112,
|
'pagedown': 34,
|
||||||
'f2': 113,
|
'page_down': 34,
|
||||||
'f3': 114,
|
'pd': 34,
|
||||||
'f4': 115,
|
|
||||||
'f5': 116,
|
|
||||||
'f6': 117,
|
|
||||||
'f7': 118,
|
|
||||||
'f8': 119,
|
|
||||||
'f9': 120,
|
|
||||||
'f10': 121,
|
|
||||||
'f11': 122,
|
|
||||||
'f12': 123
|
|
||||||
};
|
|
||||||
|
|
||||||
var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
|
'left': 37,
|
||||||
|
'up': 38,
|
||||||
|
'right': 39,
|
||||||
|
'down': 40,
|
||||||
|
|
||||||
// The event handler for bound element events
|
'f1': 112,
|
||||||
function eventHandler(e) {
|
'f2': 113,
|
||||||
e = e || $window.event;
|
'f3': 114,
|
||||||
|
'f4': 115,
|
||||||
|
'f5': 116,
|
||||||
|
'f6': 117,
|
||||||
|
'f7': 118,
|
||||||
|
'f8': 119,
|
||||||
|
'f9': 120,
|
||||||
|
'f10': 121,
|
||||||
|
'f11': 122,
|
||||||
|
'f12': 123
|
||||||
|
};
|
||||||
|
|
||||||
var code, k;
|
var isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||||
|
|
||||||
// Find out which key is pressed
|
// The event handler for bound element events
|
||||||
if (e.keyCode)
|
function eventHandler(e) {
|
||||||
{
|
e = e || $window.event;
|
||||||
code = e.keyCode;
|
|
||||||
}
|
|
||||||
else if (e.which) {
|
|
||||||
code = e.which;
|
|
||||||
}
|
|
||||||
|
|
||||||
var character = String.fromCharCode(code).toLowerCase();
|
var code, k;
|
||||||
|
|
||||||
if (code === 188){character = ",";} // If the user presses , when the type is onkeydown
|
// Find out which key is pressed
|
||||||
if (code === 190){character = ".";} // If the user presses , when the type is onkeydown
|
if (e.keyCode) {
|
||||||
|
code = e.keyCode;
|
||||||
var propagate = true;
|
} else if (e.which) {
|
||||||
|
code = e.which;
|
||||||
//Now we need to determine which shortcut this event is for, we'll do this by iterating over each
|
|
||||||
//registered shortcut to find the match. We use Find here so that the loop exits as soon
|
|
||||||
//as we've found the one we're looking for
|
|
||||||
_.find(_.keys(keyboardManagerService.keyboardEvent), function(key) {
|
|
||||||
|
|
||||||
var shortcutLabel = key;
|
|
||||||
var shortcutVal = keyboardManagerService.keyboardEvent[key];
|
|
||||||
|
|
||||||
// Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
|
|
||||||
var kp = 0;
|
|
||||||
|
|
||||||
// Some modifiers key
|
|
||||||
var modifiers = {
|
|
||||||
shift: {
|
|
||||||
wanted: false,
|
|
||||||
pressed: e.shiftKey ? true : false
|
|
||||||
},
|
|
||||||
ctrl: {
|
|
||||||
wanted: false,
|
|
||||||
pressed: e.ctrlKey ? true : false
|
|
||||||
},
|
|
||||||
alt: {
|
|
||||||
wanted: false,
|
|
||||||
pressed: e.altKey ? true : false
|
|
||||||
},
|
|
||||||
meta: { //Meta is Mac specific
|
|
||||||
wanted: false,
|
|
||||||
pressed: e.metaKey ? true : false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var keys = shortcutLabel.split("+");
|
|
||||||
var opt = shortcutVal.opt;
|
|
||||||
var callback = shortcutVal.callback;
|
|
||||||
|
|
||||||
// Foreach keys in label (split on +)
|
|
||||||
var l = keys.length;
|
|
||||||
for (var i = 0; i < l; i++) {
|
|
||||||
|
|
||||||
var k = keys[i];
|
|
||||||
switch (k) {
|
|
||||||
case 'ctrl':
|
|
||||||
case 'control':
|
|
||||||
kp++;
|
|
||||||
modifiers.ctrl.wanted = true;
|
|
||||||
break;
|
|
||||||
case 'shift':
|
|
||||||
case 'alt':
|
|
||||||
case 'meta':
|
|
||||||
kp++;
|
|
||||||
modifiers[k].wanted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k.length > 1) { // If it is a special key
|
|
||||||
if (special_keys[k] === code) {
|
|
||||||
kp++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (opt['keyCode']) { // If a specific key is set into the config
|
|
||||||
if (opt['keyCode'] === code) {
|
|
||||||
kp++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // The special keys did not match
|
|
||||||
if (character === k) {
|
|
||||||
kp++;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
|
|
||||||
character = shift_nums[character];
|
|
||||||
if (character === k) {
|
|
||||||
kp++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} //for end
|
|
||||||
|
|
||||||
if (kp === keys.length &&
|
|
||||||
modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
|
|
||||||
modifiers.shift.pressed === modifiers.shift.wanted &&
|
|
||||||
modifiers.alt.pressed === modifiers.alt.wanted &&
|
|
||||||
modifiers.meta.pressed === modifiers.meta.wanted) {
|
|
||||||
|
|
||||||
//found the right callback!
|
|
||||||
|
|
||||||
// Disable event handler when focus input and textarea
|
|
||||||
if (opt['inputDisabled']) {
|
|
||||||
var elt;
|
|
||||||
if (e.target) {
|
|
||||||
elt = e.target;
|
|
||||||
} else if (e.srcElement) {
|
|
||||||
elt = e.srcElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elt.nodeType === 3) { elt = elt.parentNode; }
|
|
||||||
if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA' || elt.hasAttribute('disable-hotkeys')) {
|
|
||||||
//This exits the Find loop
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$timeout(function () {
|
|
||||||
callback(e);
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
if (!opt['propagate']) { // Stop the event
|
|
||||||
propagate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//This exits the Find loop
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//we haven't found one so continue looking
|
|
||||||
return false;
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
// Stop the event if required
|
|
||||||
if (!propagate) {
|
|
||||||
// e.cancelBubble is supported by IE - this will kill the bubbling process.
|
|
||||||
e.cancelBubble = true;
|
|
||||||
e.returnValue = false;
|
|
||||||
|
|
||||||
// e.stopPropagation works in Firefox.
|
|
||||||
if (e.stopPropagation) {
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store all keyboard combination shortcuts
|
var character = String.fromCharCode(code).toLowerCase();
|
||||||
keyboardManagerService.keyboardEvent = {};
|
|
||||||
|
|
||||||
// Add a new keyboard combination shortcut
|
if (code === 188) {
|
||||||
keyboardManagerService.bind = function (label, callback, opt) {
|
character = ",";
|
||||||
|
} // If the user presses , when the type is onkeydown
|
||||||
|
if (code === 190) {
|
||||||
|
character = ".";
|
||||||
|
} // If the user presses , when the type is onkeydown
|
||||||
|
|
||||||
//replace ctrl key with meta key
|
var propagate = true;
|
||||||
if(isMac && label !== "ctrl+space"){
|
|
||||||
label = label.replace("ctrl","meta");
|
//Now we need to determine which shortcut this event is for, we'll do this by iterating over each
|
||||||
|
//registered shortcut to find the match. We use Find here so that the loop exits as soon
|
||||||
|
//as we've found the one we're looking for
|
||||||
|
_.find(_.keys(keyboardManagerService.keyboardEvent), function (key) {
|
||||||
|
|
||||||
|
var shortcutLabel = key;
|
||||||
|
var shortcutVal = keyboardManagerService.keyboardEvent[key];
|
||||||
|
|
||||||
|
// Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
|
||||||
|
var kp = 0;
|
||||||
|
|
||||||
|
// Some modifiers key
|
||||||
|
var modifiers = {
|
||||||
|
shift: {
|
||||||
|
wanted: false,
|
||||||
|
pressed: e.shiftKey ? true : false
|
||||||
|
},
|
||||||
|
ctrl: {
|
||||||
|
wanted: false,
|
||||||
|
pressed: e.ctrlKey ? true : false
|
||||||
|
},
|
||||||
|
alt: {
|
||||||
|
wanted: false,
|
||||||
|
pressed: e.altKey ? true : false
|
||||||
|
},
|
||||||
|
meta: { //Meta is Mac specific
|
||||||
|
wanted: false,
|
||||||
|
pressed: e.metaKey ? true : false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var keys = shortcutLabel.split("+");
|
||||||
|
var opt = shortcutVal.opt;
|
||||||
|
var callback = shortcutVal.callback;
|
||||||
|
|
||||||
|
// Foreach keys in label (split on +)
|
||||||
|
var l = keys.length;
|
||||||
|
for (var i = 0; i < l; i++) {
|
||||||
|
|
||||||
|
var k = keys[i];
|
||||||
|
switch (k) {
|
||||||
|
case 'ctrl':
|
||||||
|
case 'control':
|
||||||
|
kp++;
|
||||||
|
modifiers.ctrl.wanted = true;
|
||||||
|
break;
|
||||||
|
case 'shift':
|
||||||
|
case 'alt':
|
||||||
|
case 'meta':
|
||||||
|
kp++;
|
||||||
|
modifiers[k].wanted = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var elt;
|
if (k.length > 1) { // If it is a special key
|
||||||
// Initialize opt object
|
if (special_keys[k] === code) {
|
||||||
opt = Utilities.extend({}, defaultOpt, opt);
|
kp++;
|
||||||
label = label.toLowerCase();
|
}
|
||||||
elt = opt.target;
|
} else if (opt['keyCode']) { // If a specific key is set into the config
|
||||||
if(typeof opt.target === 'string'){
|
if (opt['keyCode'] === code) {
|
||||||
elt = document.getElementById(opt.target);
|
kp++;
|
||||||
}
|
}
|
||||||
|
} else { // The special keys did not match
|
||||||
//Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding
|
if (character === k) {
|
||||||
// and raising events for now reason. So here we'll check if the event is already registered for the element
|
kp++;
|
||||||
var boundValues = _.values(keyboardManagerService.keyboardEvent);
|
} else {
|
||||||
var found = _.find(boundValues, function (i) {
|
if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
|
||||||
return i.target === elt && i.event === opt['type'];
|
character = shift_nums[character];
|
||||||
});
|
if (character === k) {
|
||||||
|
kp++;
|
||||||
// Store shortcut
|
}
|
||||||
keyboardManagerService.keyboardEvent[label] = {
|
|
||||||
'callback': callback,
|
|
||||||
'target': elt,
|
|
||||||
'opt': opt
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
//Attach the function with the event
|
|
||||||
if (elt.addEventListener) {
|
|
||||||
elt.addEventListener(opt['type'], eventHandler, false);
|
|
||||||
} else if (elt.attachEvent) {
|
|
||||||
elt.attachEvent('on' + opt['type'], eventHandler);
|
|
||||||
} else {
|
|
||||||
elt['on' + opt['type']] = eventHandler;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
|
||||||
// Remove the shortcut - just specify the shortcut and I will remove the binding
|
|
||||||
keyboardManagerService.unbind = function (label) {
|
|
||||||
label = label.toLowerCase();
|
|
||||||
var binding = keyboardManagerService.keyboardEvent[label];
|
|
||||||
delete(keyboardManagerService.keyboardEvent[label]);
|
|
||||||
|
|
||||||
if(!binding){return;}
|
} //for end
|
||||||
|
|
||||||
var type = binding['event'],
|
if (kp === keys.length &&
|
||||||
elt = binding['target'],
|
modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
|
||||||
callback = binding['callback'];
|
modifiers.shift.pressed === modifiers.shift.wanted &&
|
||||||
|
modifiers.alt.pressed === modifiers.alt.wanted &&
|
||||||
|
modifiers.meta.pressed === modifiers.meta.wanted) {
|
||||||
|
|
||||||
if(elt.detachEvent){
|
//found the right callback!
|
||||||
elt.detachEvent('on' + type, callback);
|
|
||||||
}else if(elt.removeEventListener){
|
// Disable event handler when focus input and textarea
|
||||||
elt.removeEventListener(type, callback, false);
|
if (opt['inputDisabled']) {
|
||||||
}else{
|
var elt;
|
||||||
elt['on'+type] = false;
|
if (e.composedPath()) {
|
||||||
|
elt = e.composedPath()[0];
|
||||||
|
} else if (e.target) {
|
||||||
|
elt = e.target;
|
||||||
|
} else if (e.srcElement) {
|
||||||
|
elt = e.srcElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elt.nodeType === 3) {
|
||||||
|
elt = elt.parentNode;
|
||||||
|
}
|
||||||
|
if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA' || elt.hasAttribute('disable-hotkeys')) {
|
||||||
|
//This exits the Find loop
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
//
|
|
||||||
|
|
||||||
return keyboardManagerService;
|
$timeout(function () {
|
||||||
|
callback(e);
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
if (!opt['propagate']) { // Stop the event
|
||||||
|
propagate = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//This exits the Find loop
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//we haven't found one so continue looking
|
||||||
|
return false;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stop the event if required
|
||||||
|
if (!propagate) {
|
||||||
|
// e.cancelBubble is supported by IE - this will kill the bubbling process.
|
||||||
|
e.cancelBubble = true;
|
||||||
|
e.returnValue = false;
|
||||||
|
|
||||||
|
// e.stopPropagation works in Firefox.
|
||||||
|
if (e.stopPropagation) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store all keyboard combination shortcuts
|
||||||
|
keyboardManagerService.keyboardEvent = {};
|
||||||
|
|
||||||
|
// Add a new keyboard combination shortcut
|
||||||
|
keyboardManagerService.bind = function (label, callback, opt) {
|
||||||
|
|
||||||
|
//replace ctrl key with meta key
|
||||||
|
if (isMac && label !== "ctrl+space") {
|
||||||
|
label = label.replace("ctrl", "meta");
|
||||||
|
}
|
||||||
|
|
||||||
|
var elt;
|
||||||
|
// Initialize opt object
|
||||||
|
opt = Utilities.extend({}, defaultOpt, opt);
|
||||||
|
label = label.toLowerCase();
|
||||||
|
elt = opt.target;
|
||||||
|
if (typeof opt.target === 'string') {
|
||||||
|
elt = document.getElementById(opt.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding
|
||||||
|
// and raising events for now reason. So here we'll check if the event is already registered for the element
|
||||||
|
var boundValues = _.values(keyboardManagerService.keyboardEvent);
|
||||||
|
var found = _.find(boundValues, function (i) {
|
||||||
|
return i.target === elt && i.event === opt['type'];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store shortcut
|
||||||
|
keyboardManagerService.keyboardEvent[label] = {
|
||||||
|
'callback': callback,
|
||||||
|
'target': elt,
|
||||||
|
'opt': opt
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
//Attach the function with the event
|
||||||
|
if (elt.addEventListener) {
|
||||||
|
elt.addEventListener(opt['type'], eventHandler, false);
|
||||||
|
} else if (elt.attachEvent) {
|
||||||
|
elt.attachEvent('on' + opt['type'], eventHandler);
|
||||||
|
} else {
|
||||||
|
elt['on' + opt['type']] = eventHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
// Remove the shortcut - just specify the shortcut and I will remove the binding
|
||||||
|
keyboardManagerService.unbind = function (label) {
|
||||||
|
label = label.toLowerCase();
|
||||||
|
var binding = keyboardManagerService.keyboardEvent[label];
|
||||||
|
delete (keyboardManagerService.keyboardEvent[label]);
|
||||||
|
|
||||||
|
if (!binding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = binding['event'],
|
||||||
|
elt = binding['target'],
|
||||||
|
callback = binding['callback'];
|
||||||
|
|
||||||
|
if (elt.detachEvent) {
|
||||||
|
elt.detachEvent('on' + type, callback);
|
||||||
|
} else if (elt.removeEventListener) {
|
||||||
|
elt.removeEventListener(type, callback, false);
|
||||||
|
} else {
|
||||||
|
elt['on' + type] = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//
|
||||||
|
|
||||||
|
return keyboardManagerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]);
|
angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]);
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ h6.-black {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
umb-property:last-of-type .umb-control-group {
|
umb-property:last-of-type > .umb-property > ng-form > .umb-control-group {
|
||||||
&::after {
|
&::after {
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
|||||||
@@ -118,6 +118,11 @@
|
|||||||
line-height: 36px;
|
line-height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-overlay .btn-social > .umb-icon {
|
||||||
|
padding: 2px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.login-overlay .text-error,
|
.login-overlay .text-error,
|
||||||
.login-overlay .text-info
|
.login-overlay .text-info
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -128,6 +128,13 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
|
|||||||
eventsService.emit("dialogs.linkPicker.select", args);
|
eventsService.emit("dialogs.linkPicker.select", args);
|
||||||
|
|
||||||
if ($scope.currentNode) {
|
if ($scope.currentNode) {
|
||||||
|
if ($scope.currentNode.id == args.node.id && $scope.currentNode.selected) {
|
||||||
|
$scope.model.target = {};
|
||||||
|
$scope.currentNode.selected = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//un-select if there's a current one selected
|
//un-select if there's a current one selected
|
||||||
$scope.currentNode.selected = false;
|
$scope.currentNode.selected = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
<umb-property-editor
|
<umb-property-editor
|
||||||
model="property"
|
model="property"
|
||||||
node="contentNodeModel"
|
node="contentNodeModel"
|
||||||
preview="(propertyEditorDisabled(property) && allowUpdate) || (!allowUpdate && !property.supportsReadOnly)"
|
preview="((property.readonly || !allowUpdate) && !property.supportsReadOnly) || (propertyEditorDisabled(property) && allowUpdate)"
|
||||||
allow-unlock="allowUpdate && allowEditInvariantFromNonDefault"
|
allow-unlock="!property.readonly && allowUpdate && allowEditInvariantFromNonDefault"
|
||||||
on-unlock="unlockInvariantValue(property)"
|
on-unlock="unlockInvariantValue(property)"
|
||||||
ng-attr-readonly="{{ !allowUpdate || undefined}}">
|
ng-attr-readonly="{{property.readonly || !allowUpdate || undefined}}">
|
||||||
</umb-property-editor>
|
</umb-property-editor>
|
||||||
|
|
||||||
</umb-property>
|
</umb-property>
|
||||||
@@ -52,10 +52,10 @@
|
|||||||
<umb-property-editor
|
<umb-property-editor
|
||||||
model="property"
|
model="property"
|
||||||
node="contentNodeModel"
|
node="contentNodeModel"
|
||||||
preview="(propertyEditorDisabled(property) && allowUpdate) || (!allowUpdate && !property.supportsReadOnly)"
|
preview="((property.readonly || !allowUpdate) && !property.supportsReadOnly) || (propertyEditorDisabled(property) && allowUpdate)"
|
||||||
allow-unlock="allowUpdate && allowEditInvariantFromNonDefault"
|
allow-unlock="!property.readonly && allowUpdate && allowEditInvariantFromNonDefault"
|
||||||
on-unlock="unlockInvariantValue(property)"
|
on-unlock="unlockInvariantValue(property)"
|
||||||
ng-attr-readonly="{{!allowUpdate || undefined}}">
|
ng-attr-readonly="{{property.readonly || !allowUpdate || undefined}}">
|
||||||
</umb-property-editor>
|
</umb-property-editor>
|
||||||
|
|
||||||
</umb-property>
|
</umb-property>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
ng-model="model.value"
|
ng-model="model.value"
|
||||||
val-server="value"
|
val-server="value"
|
||||||
min="0"
|
min="0"
|
||||||
max="500"
|
max="512"
|
||||||
fix-number />
|
fix-number />
|
||||||
|
|
||||||
<span ng-messages="prevalueTextLimitedForm.textLimitedField.$error" show-validation-on-submit >
|
<span ng-messages="prevalueTextLimitedForm.textLimitedField.$error" show-validation-on-submit >
|
||||||
@@ -13,5 +13,5 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
</ng-form>
|
</ng-form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
`
|
`
|
||||||
${ model.stylesheet ? `
|
${ model.stylesheet ? `
|
||||||
<style>
|
<style>
|
||||||
@import "${model.stylesheet}"
|
@import "${model.stylesheet}?umb__rnd=${Umbraco.Sys.ServerVariables.application.cacheBuster}"
|
||||||
</style>`
|
</style>`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,8 @@
|
|||||||
shadowRoot.innerHTML =
|
shadowRoot.innerHTML =
|
||||||
`
|
`
|
||||||
<style>
|
<style>
|
||||||
{{vm.stylesheet ? "@import '"+vm.stylesheet+"';" : ""}}
|
{{vm.stylesheet ? "@import '" + vm.stylesheet + "?umb__rnd=${Umbraco.Sys.ServerVariables.application.cacheBuster}';" : ""}}
|
||||||
@import 'assets/css/blockgridui.css';
|
@import 'assets/css/blockgridui.css?umb__rnd=${Umbraco.Sys.ServerVariables.application.cacheBuster}';
|
||||||
:host {
|
:host {
|
||||||
--umb-block-grid--grid-columns: ${vm.gridColumns};
|
--umb-block-grid--grid-columns: ${vm.gridColumns};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
border-radius: @baseBorderRadius;
|
border-radius: @baseBorderRadius;
|
||||||
transition: border-color 120ms, background-color 120ms;
|
transition: border-color 120ms, background-color 120ms;
|
||||||
|
|
||||||
|
.umb-box {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.umb-block-list__block:not(.--active) &:hover {
|
.umb-block-list__block:not(.--active) &:hover {
|
||||||
border-color: @gray-8;
|
border-color: @gray-8;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
ng-click="openCurrentPicker()"
|
ng-click="openCurrentPicker()"
|
||||||
id="{{model.alias}}"
|
id="{{model.alias}}"
|
||||||
aria-label="{{model.label}}: {{labels.general_add}}"
|
aria-label="{{model.label}}: {{labels.general_add}}"
|
||||||
ng-disabled="readonly">
|
ng-disabled="!allowAdd">
|
||||||
<localize key="general_add">Add</localize>
|
<localize key="general_add">Add</localize>
|
||||||
<span class="sr-only">...</span>
|
<span class="sr-only">...</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
30
tests/Umbraco.Tests.AcceptanceTest/package-lock.json
generated
30
tests/Umbraco.Tests.AcceptanceTest/package-lock.json
generated
@@ -17,7 +17,7 @@
|
|||||||
"xhr2": "^0.2.1"
|
"xhr2": "^0.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.31",
|
"@playwright/test": "^1.32",
|
||||||
"del": "^6.0.0",
|
"del": "^6.0.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"prompt": "^1.2.0",
|
"prompt": "^1.2.0",
|
||||||
@@ -86,13 +86,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.31.1",
|
"version": "1.32.3",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
|
||||||
"integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==",
|
"integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"playwright-core": "1.31.1"
|
"playwright-core": "1.32.3"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@@ -742,9 +742,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.31.1",
|
"version": "1.32.3",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz",
|
||||||
"integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==",
|
"integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
@@ -1035,14 +1035,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@playwright/test": {
|
"@playwright/test": {
|
||||||
"version": "1.31.1",
|
"version": "1.32.3",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.31.1.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.32.3.tgz",
|
||||||
"integrity": "sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==",
|
"integrity": "sha512-BvWNvK0RfBriindxhLVabi8BRe3X0J9EVjKlcmhxjg4giWBD/xleLcg2dz7Tx0agu28rczjNIPQWznwzDwVsZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"fsevents": "2.3.2",
|
"fsevents": "2.3.2",
|
||||||
"playwright-core": "1.31.1"
|
"playwright-core": "1.32.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sideway/address": {
|
"@sideway/address": {
|
||||||
@@ -1528,9 +1528,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"playwright-core": {
|
"playwright-core": {
|
||||||
"version": "1.31.1",
|
"version": "1.32.3",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.1.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.3.tgz",
|
||||||
"integrity": "sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==",
|
"integrity": "sha512-SB+cdrnu74ZIn5Ogh/8278ngEh9NEEV0vR4sJFmK04h2iZpybfbqBY0bX6+BLYWVdV12JLLI+JEFtSnYgR+mWg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prompt": {
|
"prompt": {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"createTest": "node createTest.js"
|
"createTest": "node createTest.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.31",
|
"@playwright/test": "^1.32",
|
||||||
"typescript": "^4.8.3",
|
"typescript": "^4.8.3",
|
||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"del": "^6.0.0",
|
"del": "^6.0.0",
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ test.describe('BlockGridEditorInDocument', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createDefaultDocumentTypeWithBlockGridEditor(umbracoApi, BlockGridEditor?) {
|
async function createDefaultDocumentTypeWithBlockGridEditor(umbracoApi, BlockGridEditor?) {
|
||||||
|
|
||||||
const rootDocType = new DocumentTypeBuilder()
|
const rootDocType = new DocumentTypeBuilder()
|
||||||
.withName(documentName)
|
.withName(documentName)
|
||||||
.withAllowAsRoot(true)
|
.withAllowAsRoot(true)
|
||||||
@@ -269,4 +268,4 @@ test.describe('BlockGridEditorInDocument', () => {
|
|||||||
// Clean
|
// Clean
|
||||||
await umbracoApi.documentTypes.ensureNameNotExists(elementName);
|
await umbracoApi.documentTypes.ensureNameNotExists(elementName);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {ContentBuilder, DocumentTypeBuilder, PartialViewBuilder} from "@umbraco/
|
|||||||
import {BlockListDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes";
|
import {BlockListDataTypeBuilder} from "@umbraco/json-models-builders/dist/lib/builders/dataTypes";
|
||||||
|
|
||||||
test.describe('BlockListEditorContent', () => {
|
test.describe('BlockListEditorContent', () => {
|
||||||
|
|
||||||
const documentName = 'DocumentTestName';
|
const documentName = 'DocumentTestName';
|
||||||
const blockListName = 'BlockListTest';
|
const blockListName = 'BlockListTest';
|
||||||
const elementName = 'TestElement';
|
const elementName = 'TestElement';
|
||||||
@@ -126,6 +125,9 @@ test.describe('BlockListEditorContent', () => {
|
|||||||
// Checks if the content was created
|
// Checks if the content was created
|
||||||
await expect(page.locator('.umb-block-list__block--view')).toHaveCount(1);
|
await expect(page.locator('.umb-block-list__block--view')).toHaveCount(1);
|
||||||
await expect(page.locator('.umb-block-list__block--view').nth(0)).toHaveText(elementName);
|
await expect(page.locator('.umb-block-list__block--view').nth(0)).toHaveText(elementName);
|
||||||
|
// Checks if the content contains the correct value
|
||||||
|
await page.locator('.umb-block-list__block--view').nth(0).click();
|
||||||
|
await expect(page.locator('[id="sub-view-0"]').locator('[id="title"]')).toHaveValue('Testing...');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can update content with a block list editor', async ({page, umbracoApi, umbracoUi}) => {
|
test('can update content with a block list editor', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
@@ -385,7 +387,7 @@ test.describe('BlockListEditorContent', () => {
|
|||||||
|
|
||||||
test('can see rendered content with a block list editor', async ({page, umbracoApi, umbracoUi}) => {
|
test('can see rendered content with a block list editor', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
await umbracoApi.templates.ensureNameNotExists(documentName);
|
await umbracoApi.templates.ensureNameNotExists(documentName);
|
||||||
await umbracoApi.partialViews.ensureNameNotExists(elementName + '.cshtml');
|
await umbracoApi.partialViews.ensureNameNotExists('blocklist/Components',elementAlias + '.cshtml');
|
||||||
|
|
||||||
const element = new DocumentTypeBuilder()
|
const element = new DocumentTypeBuilder()
|
||||||
.withName(elementName)
|
.withName(elementName)
|
||||||
@@ -480,6 +482,6 @@ test.describe('BlockListEditorContent', () => {
|
|||||||
|
|
||||||
// Clean
|
// Clean
|
||||||
await umbracoApi.templates.ensureNameNotExists(documentName);
|
await umbracoApi.templates.ensureNameNotExists(documentName);
|
||||||
await umbracoApi.partialViews.ensureNameNotExists(elementAlias + '.cshtml');
|
await umbracoApi.partialViews.ensureNameNotExists('blocklist/Components',elementAlias + '.cshtml');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -28,7 +28,7 @@ test.describe('Languages', () => {
|
|||||||
|
|
||||||
const doesExistDA = await umbracoApi.languages.exists(culture);
|
const doesExistDA = await umbracoApi.languages.exists(culture);
|
||||||
await expect(doesExistDA).toBe(true);
|
await expect(doesExistDA).toBe(true);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
await umbracoApi.languages.ensureCultureNotExists(culture);
|
await umbracoApi.languages.ensureCultureNotExists(culture);
|
||||||
});
|
});
|
||||||
@@ -70,7 +70,7 @@ test.describe('Languages', () => {
|
|||||||
await expect(page.getByRole('button', {name: language2})).not.toBeVisible();
|
await expect(page.getByRole('button', {name: language2})).not.toBeVisible();
|
||||||
doesExistEN = await umbracoApi.languages.exists(language2);
|
doesExistEN = await umbracoApi.languages.exists(language2);
|
||||||
await expect(doesExistEN).toBe(false);
|
await expect(doesExistEN).toBe(false);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
await umbracoApi.languages.ensureCultureNotExists(language1);
|
await umbracoApi.languages.ensureCultureNotExists(language1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ test.describe('media File Types', () => {
|
|||||||
await umbracoUi.goToSection(ConstantHelper.sections.media);
|
await umbracoUi.goToSection(ConstantHelper.sections.media);
|
||||||
await umbracoApi.media.deleteAllMedia();
|
await umbracoApi.media.deleteAllMedia();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.describe('create each File Types', () => {
|
test.describe('create each File Types', () => {
|
||||||
test('create Article', async ({page, umbracoApi, umbracoUi}) => {
|
test('create Article', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const articleName = "Article";
|
const articleName = "Article";
|
||||||
@@ -53,7 +53,7 @@ test.describe('media File Types', () => {
|
|||||||
const path = 'mediaLibrary/' + fileName;
|
const path = 'mediaLibrary/' + fileName;
|
||||||
const mimeType = "*/*";
|
const mimeType = "*/*";
|
||||||
await umbracoApi.media.ensureNameNotExists(fileItemName);
|
await umbracoApi.media.ensureNameNotExists(fileItemName);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
await umbracoApi.media.createFileWithFile(fileItemName, fileName, path, mimeType);
|
await umbracoApi.media.createFileWithFile(fileItemName, fileName, path, mimeType);
|
||||||
await umbracoUi.refreshMediaTree();
|
await umbracoUi.refreshMediaTree();
|
||||||
@@ -222,7 +222,7 @@ test.describe('media File Types', () => {
|
|||||||
const childName = 'ChildFolder';
|
const childName = 'ChildFolder';
|
||||||
await umbracoApi.media.ensureNameNotExists(parentName);
|
await umbracoApi.media.ensureNameNotExists(parentName);
|
||||||
await umbracoApi.media.ensureNameNotExists(childName);
|
await umbracoApi.media.ensureNameNotExists(childName);
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
await umbracoApi.media.createDefaultFolder(parentName);
|
await umbracoApi.media.createDefaultFolder(parentName);
|
||||||
await umbracoUi.refreshMediaTree();
|
await umbracoUi.refreshMediaTree();
|
||||||
@@ -325,7 +325,7 @@ test.describe('media File Types', () => {
|
|||||||
await umbracoApi.media.ensureNameNotExists(childName);
|
await umbracoApi.media.ensureNameNotExists(childName);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Delete one of each Files in media', async ({page, umbracoApi, umbracoUi}) => {
|
test('Delete one of each Files in media', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const articleName = 'ArticleToDelete';
|
const articleName = 'ArticleToDelete';
|
||||||
const audioName = 'AudioToDelete';
|
const audioName = 'AudioToDelete';
|
||||||
@@ -341,7 +341,7 @@ test.describe('media File Types', () => {
|
|||||||
await page.reload();
|
await page.reload();
|
||||||
// Needs to close tours when page has reloaded
|
// Needs to close tours when page has reloaded
|
||||||
await page.click('.umb-tour-step__close');
|
await page.click('.umb-tour-step__close');
|
||||||
|
|
||||||
// Takes all the child elements in folder-grid.
|
// Takes all the child elements in folder-grid.
|
||||||
await page.locator(".umb-folder-grid").locator("xpath=/*", {hasText: folderName}).click({
|
await page.locator(".umb-folder-grid").locator("xpath=/*", {hasText: folderName}).click({
|
||||||
position: {
|
position: {
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ test.describe('Media', () => {
|
|||||||
await umbracoApi.media.deleteAllMedia();
|
await umbracoApi.media.deleteAllMedia();
|
||||||
await umbracoApi.media.clearRecycleBin();
|
await umbracoApi.media.clearRecycleBin();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.afterEach(async ({page, umbracoApi, umbracoUi}, testInfo) => {
|
test.afterEach(async ({page, umbracoApi, umbracoUi}, testInfo) => {
|
||||||
await umbracoApi.media.deleteAllMedia();
|
await umbracoApi.media.deleteAllMedia();
|
||||||
await umbracoApi.media.clearRecycleBin();
|
await umbracoApi.media.clearRecycleBin();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('move one of each Files into a Folder', async ({page, umbracoApi, umbracoUi}) => {
|
test('move one of each Files into a Folder', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const articleName = 'ArticleToMove';
|
const articleName = 'ArticleToMove';
|
||||||
const audioName = 'AudioToMove';
|
const audioName = 'AudioToMove';
|
||||||
@@ -25,7 +25,7 @@ test.describe('Media', () => {
|
|||||||
const vectorGraphicsName = 'VectorGraphicsToMove';
|
const vectorGraphicsName = 'VectorGraphicsToMove';
|
||||||
const videoName = 'VideoToMove';
|
const videoName = 'VideoToMove';
|
||||||
const folderToMoveTooName = 'MoveHere';
|
const folderToMoveTooName = 'MoveHere';
|
||||||
|
|
||||||
const mediaFileTypes = [
|
const mediaFileTypes = [
|
||||||
{fileTypeNames: articleName},
|
{fileTypeNames: articleName},
|
||||||
{fileTypeNames: audioName},
|
{fileTypeNames: audioName},
|
||||||
@@ -34,7 +34,7 @@ test.describe('Media', () => {
|
|||||||
{fileTypeNames: vectorGraphicsName},
|
{fileTypeNames: vectorGraphicsName},
|
||||||
{fileTypeNames: videoName}
|
{fileTypeNames: videoName}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
await umbracoApi.media.createAllFileTypes(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName);
|
await umbracoApi.media.createAllFileTypes(articleName, audioName, fileName, folderName, imageName, vectorGraphicsName, videoName);
|
||||||
await umbracoApi.media.createDefaultFolder(folderToMoveTooName);
|
await umbracoApi.media.createDefaultFolder(folderToMoveTooName);
|
||||||
@@ -53,7 +53,7 @@ test.describe('Media', () => {
|
|||||||
await page.locator('[label-key="actions_move"]').click();
|
await page.locator('[label-key="actions_move"]').click();
|
||||||
await page.locator('[data-element="editor-container"] >> "' + folderToMoveTooName + '"').click();
|
await page.locator('[data-element="editor-container"] >> "' + folderToMoveTooName + '"').click();
|
||||||
await page.locator('[label-key="general_submit"]').click();
|
await page.locator('[label-key="general_submit"]').click();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Needs to wait before refreshing the media tree, otherwise the media files wont be moved to the folder yet
|
// Needs to wait before refreshing the media tree, otherwise the media files wont be moved to the folder yet
|
||||||
await page.waitForTimeout(2500);
|
await page.waitForTimeout(2500);
|
||||||
@@ -64,12 +64,12 @@ test.describe('Media', () => {
|
|||||||
}
|
}
|
||||||
await expect(page.locator(".umb-folder-grid", {hasText: folderName})).toBeVisible();
|
await expect(page.locator(".umb-folder-grid", {hasText: folderName})).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sort by Name', async ({page, umbracoApi, umbracoUi}) => {
|
test('sort by Name', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const FolderNameA = 'A';
|
const FolderNameA = 'A';
|
||||||
const FolderNameB = 'B';
|
const FolderNameB = 'B';
|
||||||
const FolderNameC = 'C';
|
const FolderNameC = 'C';
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
await umbracoApi.media.createDefaultFolder(FolderNameC);
|
await umbracoApi.media.createDefaultFolder(FolderNameC);
|
||||||
await umbracoApi.media.createDefaultFolder(FolderNameB);
|
await umbracoApi.media.createDefaultFolder(FolderNameB);
|
||||||
@@ -130,4 +130,4 @@ test.describe('Media', () => {
|
|||||||
// Assert
|
// Assert
|
||||||
await expect(page.locator('[icon="icon-thumbnails-small"]')).toBeVisible();
|
await expect(page.locator('[icon="icon-thumbnails-small"]')).toBeVisible();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
.done()
|
.done()
|
||||||
.build();
|
.build();
|
||||||
await umbracoApi.documentTypes.save(docType);
|
await umbracoApi.documentTypes.save(docType);
|
||||||
|
|
||||||
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
||||||
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@@ -81,7 +81,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
.done()
|
.done()
|
||||||
.build();
|
.build();
|
||||||
const savedDocType = await umbracoApi.documentTypes.save(docType);
|
const savedDocType = await umbracoApi.documentTypes.save(docType);
|
||||||
|
|
||||||
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
||||||
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@@ -150,7 +150,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
.done()
|
.done()
|
||||||
.build();
|
.build();
|
||||||
const savedDocType = await umbracoApi.documentTypes.save(docType);
|
const savedDocType = await umbracoApi.documentTypes.save(docType);
|
||||||
|
|
||||||
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
||||||
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@@ -183,7 +183,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
// We only have to type out the opening tag, the editor adds the closing tag automatically.
|
// We only have to type out the opening tag, the editor adds the closing tag automatically.
|
||||||
await editor.type("<p>Edited");
|
await editor.type("<p>Edited");
|
||||||
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save));
|
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save));
|
||||||
|
|
||||||
await umbracoUi.isSuccessNotificationVisible({timeout:10000});
|
await umbracoUi.isSuccessNotificationVisible({timeout:10000});
|
||||||
|
|
||||||
await umbracoApi.content.verifyRenderedContent("/", "<h1>" + propertyValue + "</h1><p>Edited</p>", true);
|
await umbracoApi.content.verifyRenderedContent("/", "<h1>" + propertyValue + "</h1><p>Edited</p>", true);
|
||||||
@@ -195,7 +195,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
|
|
||||||
test('Can update view and document type', async ({page, umbracoApi, umbracoUi},testInfo) => {
|
test('Can update view and document type', async ({page, umbracoApi, umbracoUi},testInfo) => {
|
||||||
await testInfo.slow();
|
await testInfo.slow();
|
||||||
|
|
||||||
const docTypeName = "TestDocument";
|
const docTypeName = "TestDocument";
|
||||||
const docTypeAlias = AliasHelper.toAlias(docTypeName);
|
const docTypeAlias = AliasHelper.toAlias(docTypeName);
|
||||||
const propertyAlias = "title";
|
const propertyAlias = "title";
|
||||||
@@ -219,7 +219,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
.done()
|
.done()
|
||||||
.build();
|
.build();
|
||||||
const savedDocType = await umbracoApi.documentTypes.save(docType);
|
const savedDocType = await umbracoApi.documentTypes.save(docType);
|
||||||
|
|
||||||
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
await umbracoApi.templates.edit(docTypeName, `@using Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Testdocument>
|
||||||
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;
|
||||||
@@ -276,7 +276,7 @@ test.describe('Modelsbuilder tests', () => {
|
|||||||
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish));
|
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.saveAndPublish));
|
||||||
|
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await umbracoApi.content.verifyRenderedContent("/", "<h1>" + propertyValue + "</h1><p>Fancy body text</p>", true);
|
await umbracoApi.content.verifyRenderedContent("/", "<h1>" + propertyValue + "</h1><p>Fancy body text</p>", true);
|
||||||
|
|
||||||
await umbracoApi.content.deleteAllContent();
|
await umbracoApi.content.deleteAllContent();
|
||||||
|
|||||||
@@ -37,18 +37,16 @@ test.describe('Partial Views', () => {
|
|||||||
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save));
|
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save));
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
await umbracoUi.isSuccessNotificationVisible({timeout: 20000});
|
await umbracoUi.isSuccessNotificationVisible({timeout: 30000});
|
||||||
|
|
||||||
//Clean up
|
//Clean up
|
||||||
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Create partial view from snippet', async ({page, umbracoApi, umbracoUi}) => {
|
test('Create partial view from snippet', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const name = "TestPartialViewFromSnippet";
|
const name = "TestPartialViewFromSnippet";
|
||||||
const fileName = name + ".cshtml";
|
const fileName = name + ".cshtml";
|
||||||
|
|
||||||
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
||||||
|
|
||||||
await openPartialViewsCreatePanel(page, umbracoUi);
|
await openPartialViewsCreatePanel(page, umbracoUi);
|
||||||
|
|
||||||
await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create"));
|
await umbracoUi.clickElement(umbracoUi.getContextMenuAction("action-create"));
|
||||||
@@ -61,14 +59,13 @@ test.describe('Partial Views', () => {
|
|||||||
|
|
||||||
// Save
|
// Save
|
||||||
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save));
|
await umbracoUi.clickElement(umbracoUi.getButtonByLabelKey(ConstantHelper.buttons.save));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await umbracoUi.isSuccessNotificationVisible({timeout:20000});
|
await umbracoUi.isSuccessNotificationVisible({timeout:20000});
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Partial view with no name', async ({page, umbracoApi, umbracoUi}) => {
|
test('Partial view with no name', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
await openPartialViewsCreatePanel(page, umbracoUi);
|
await openPartialViewsCreatePanel(page, umbracoUi);
|
||||||
|
|
||||||
@@ -84,8 +81,8 @@ test.describe('Partial Views', () => {
|
|||||||
|
|
||||||
// Asserts
|
// Asserts
|
||||||
await umbracoUi.isErrorNotificationVisible();
|
await umbracoUi.isErrorNotificationVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Delete partial view', async ({page, umbracoApi, umbracoUi}) => {
|
test('Delete partial view', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const name = "TestDeletePartialView";
|
const name = "TestDeletePartialView";
|
||||||
const fileName = name + ".cshtml";
|
const fileName = name + ".cshtml";
|
||||||
@@ -112,8 +109,7 @@ test.describe('Partial Views', () => {
|
|||||||
|
|
||||||
// Clean
|
// Clean
|
||||||
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Edit partial view', async ({page, umbracoApi, umbracoUi}) => {
|
test('Edit partial view', async ({page, umbracoApi, umbracoUi}) => {
|
||||||
const name = 'EditPartialView';
|
const name = 'EditPartialView';
|
||||||
const fileName = name + ".cshtml";
|
const fileName = name + ".cshtml";
|
||||||
@@ -136,7 +132,6 @@ test.describe('Partial Views', () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await umbracoUi.isSuccessNotificationVisible({timeout:20000});
|
await umbracoUi.isSuccessNotificationVisible({timeout:20000});
|
||||||
|
|
||||||
// Clean
|
// Clean
|
||||||
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
await umbracoApi.partialViews.ensureNameNotExists('', fileName);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ test.describe('Tours', () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test('Backoffice introduction tour should run', async ({page, umbracoApi, umbracoUi}) => {
|
test('Backoffice introduction tour should run', async ({page, umbracoApi, umbracoUi}, testInfo) => {
|
||||||
|
await testInfo.slow();
|
||||||
|
|
||||||
// We have to reload this page, as we already get a page context after login
|
// We have to reload this page, as we already get a page context after login
|
||||||
// before we have reset a users tour data
|
// before we have reset a users tour data
|
||||||
await expect(await umbracoUi.getGlobalHelp()).toBeVisible();
|
await expect(await umbracoUi.getGlobalHelp()).toBeVisible();
|
||||||
@@ -87,7 +89,9 @@ test.describe('Tours', () => {
|
|||||||
await getPercentage(17, timeout, page);
|
await getPercentage(17, timeout, page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Backoffice introduction tour should run, then rerun', async ({page, umbracoApi, umbracoUi}) => {
|
test('Backoffice introduction tour should run, then rerun', async ({page, umbracoApi, umbracoUi}, testInfo) => {
|
||||||
|
await testInfo.slow();
|
||||||
|
|
||||||
await expect(await umbracoUi.getGlobalHelp()).toBeVisible();
|
await expect(await umbracoUi.getGlobalHelp()).toBeVisible();
|
||||||
await umbracoUi.clickElement(umbracoUi.getGlobalHelp());
|
await umbracoUi.clickElement(umbracoUi.getGlobalHelp());
|
||||||
await runBackOfficeIntroTour(0, 'Start', timeout, page, umbracoUi);
|
await runBackOfficeIntroTour(0, 'Start', timeout, page, umbracoUi);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using Umbraco.Cms.Infrastructure.Scoping;
|
|||||||
using Umbraco.Cms.Tests.Common.Builders;
|
using Umbraco.Cms.Tests.Common.Builders;
|
||||||
using Umbraco.Cms.Tests.Common.Testing;
|
using Umbraco.Cms.Tests.Common.Testing;
|
||||||
using Umbraco.Cms.Tests.Integration.Testing;
|
using Umbraco.Cms.Tests.Integration.Testing;
|
||||||
|
using static Umbraco.Cms.Core.Constants.Conventions;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositories;
|
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositories;
|
||||||
|
|
||||||
@@ -98,6 +99,45 @@ public class TagRepositoryTest : UmbracoIntegrationTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Can_Create_Tag_Relations_With_Mixed_Casing()
|
||||||
|
{
|
||||||
|
var provider = ScopeProvider;
|
||||||
|
using (ScopeProvider.CreateScope())
|
||||||
|
{
|
||||||
|
var template = TemplateBuilder.CreateTextPageTemplate();
|
||||||
|
FileService.SaveTemplate(template);
|
||||||
|
|
||||||
|
var contentType =
|
||||||
|
ContentTypeBuilder.CreateSimpleContentType("test", "Test", defaultTemplateId: template.Id);
|
||||||
|
ContentTypeRepository.Save(contentType);
|
||||||
|
|
||||||
|
var content1 = ContentBuilder.CreateSimpleContent(contentType);
|
||||||
|
var content2 = ContentBuilder.CreateSimpleContent(contentType);
|
||||||
|
DocumentRepository.Save(content1);
|
||||||
|
DocumentRepository.Save(content2);
|
||||||
|
|
||||||
|
var repository = CreateRepository(provider);
|
||||||
|
Tag[] tags1 = { new Tag { Text = "tag1", Group = "test" } };
|
||||||
|
repository.Assign(
|
||||||
|
content1.Id,
|
||||||
|
contentType.PropertyTypes.First().Id,
|
||||||
|
tags1,
|
||||||
|
false);
|
||||||
|
|
||||||
|
// Note the casing is different from tags1, but both should be considered equivalent
|
||||||
|
Tag[] tags2 = { new Tag { Text = "TAG1", Group = "test" } };
|
||||||
|
repository.Assign(
|
||||||
|
content2.Id,
|
||||||
|
contentType.PropertyTypes.First().Id,
|
||||||
|
tags2,
|
||||||
|
false);
|
||||||
|
|
||||||
|
// The template should have only one tag, despite case differences
|
||||||
|
Assert.AreEqual(1, repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Content, "tag1").Count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Can_Append_Tag_Relations()
|
public void Can_Append_Tag_Relations()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,66 +7,52 @@ using Umbraco.Cms.Imaging.ImageSharp.Media;
|
|||||||
|
|
||||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Media;
|
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Media;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains tests for all parameters for image generation options.
|
||||||
|
/// </summary>
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ImageSharpImageUrlGeneratorTests
|
public class ImageSharpImageUrlGeneratorTests
|
||||||
{
|
{
|
||||||
private const string MediaPath = "/media/1005/img_0671.jpg";
|
private const string MediaPath = "/media/1005/img_0671.jpg";
|
||||||
|
|
||||||
private static readonly ImageUrlGenerationOptions.CropCoordinates s_crop = new(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m);
|
private static readonly ImageUrlGenerationOptions.CropCoordinates _sCrop = new(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m);
|
||||||
|
private static readonly ImageUrlGenerationOptions.FocalPointPosition _sFocus = new(0.96m, 0.80827067669172936m);
|
||||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus1 = new(0.96m, 0.80827067669172936m);
|
private static readonly ImageSharpImageUrlGenerator _sGenerator = new(Array.Empty<string>());
|
||||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition s_focus2 = new(0.4275m, 0.41m);
|
|
||||||
private static readonly ImageSharpImageUrlGenerator s_generator = new(new string[0]);
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that the media path is returned if no options are provided.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrl_CropAliasTest()
|
public void GivenMediaPath_AndNoOptions_ReturnsMediaPath()
|
||||||
{
|
{
|
||||||
var urlString =
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath));
|
||||||
s_generator.GetImageUrl(
|
Assert.AreEqual(MediaPath, actual);
|
||||||
new ImageUrlGenerationOptions(MediaPath) { Crop = s_crop, Width = 100, Height = 100 });
|
|
||||||
Assert.AreEqual(
|
|
||||||
MediaPath + "?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100",
|
|
||||||
urlString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test that if options is null, the generated image URL is also null.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrl_WidthHeightTest()
|
public void GivenNullOptions_ReturnsNull()
|
||||||
{
|
{
|
||||||
var urlString =
|
var actual = _sGenerator.GetImageUrl(null);
|
||||||
s_generator.GetImageUrl(
|
Assert.IsNull(actual);
|
||||||
new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 200, Height = 300 });
|
|
||||||
Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=200&height=300", urlString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test that if a null image url is given, null is returned.
|
||||||
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrl_FocalPointTest()
|
public void GivenNullImageUrl_ReturnsNull()
|
||||||
{
|
{
|
||||||
var urlString =
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(null));
|
||||||
s_generator.GetImageUrl(
|
Assert.IsNull(actual);
|
||||||
new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus1, Width = 100, Height = 100 });
|
|
||||||
Assert.AreEqual(MediaPath + "?rxy=0.96,0.80827067669172936&width=100&height=100", urlString);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void GetImageUrlFurtherOptionsTest()
|
|
||||||
{
|
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
|
||||||
{
|
|
||||||
FocalPoint = s_focus1,
|
|
||||||
Width = 200,
|
|
||||||
Height = 300,
|
|
||||||
FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff",
|
|
||||||
});
|
|
||||||
Assert.AreEqual(
|
|
||||||
MediaPath +
|
|
||||||
"?rxy=0.96,0.80827067669172936&width=200&height=300&filter=comic&roundedcorners=radius-26%7Cbgcolor-fff",
|
|
||||||
urlString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrlFurtherOptionsModeAndQualityTest()
|
public void GetImageUrlFurtherOptionsModeAndQualityTest()
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
var urlString = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
||||||
{
|
{
|
||||||
Quality = 10,
|
Quality = 10,
|
||||||
FurtherOptions = "format=webp",
|
FurtherOptions = "format=webp",
|
||||||
@@ -80,7 +66,7 @@ public class ImageSharpImageUrlGeneratorTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrlFurtherOptionsWithModeAndQualityTest()
|
public void GetImageUrlFurtherOptionsWithModeAndQualityTest()
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
var urlString = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
||||||
{
|
{
|
||||||
FurtherOptions = "quality=10&format=webp",
|
FurtherOptions = "quality=10&format=webp",
|
||||||
});
|
});
|
||||||
@@ -91,169 +77,171 @@ public class ImageSharpImageUrlGeneratorTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test that if options is null, the generated image URL is also null.
|
/// Test that if an empty string image url is given, null is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrlNullOptionsTest()
|
public void GivenEmptyStringImageUrl_ReturnsEmptyString()
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(null);
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty));
|
||||||
Assert.AreEqual(null, urlString);
|
Assert.AreEqual(actual, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test that if the image URL is null, the generated image URL is also null.
|
/// Tests the correct query string is returned when given a crop.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrlNullTest()
|
public void GivenCrop_ReturnsExpectedQueryString()
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(null));
|
const string expected = "?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386";
|
||||||
Assert.AreEqual(null, urlString);
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty) { Crop = _sCrop });
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test that if the image URL is empty, the generated image URL is empty.
|
/// Tests the correct query string is returned when given a width.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrlEmptyTest()
|
public void GivenWidth_ReturnsExpectedQueryString()
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty));
|
const string expected = "?width=200";
|
||||||
Assert.AreEqual(string.Empty, urlString);
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty) { Width = 200 });
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test the GetImageUrl method on the ImageCropDataSet Model
|
/// Tests the correct query string is returned when given a height.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetBaseCropUrlFromModelTest()
|
public void GivenHeight_ReturnsExpectedQueryString()
|
||||||
{
|
{
|
||||||
var urlString =
|
const string expected = "?height=200";
|
||||||
s_generator.GetImageUrl(
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty) { Height = 200 });
|
||||||
new ImageUrlGenerationOptions(string.Empty) { Crop = s_crop, Width = 100, Height = 100 });
|
Assert.AreEqual(expected, actual);
|
||||||
Assert.AreEqual(
|
|
||||||
"?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&width=100&height=100",
|
|
||||||
urlString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test that if Crop mode is specified as anything other than Crop the image doesn't use the crop
|
/// Tests the correct query string is returned when provided a focal point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void GetImageUrl_SpecifiedCropModeTest()
|
public void GivenFocalPoint_ReturnsExpectedQueryString()
|
||||||
{
|
{
|
||||||
var urlStringMin =
|
const string expected = "?rxy=0.96,0.80827067669172936";
|
||||||
s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty) { FocalPoint = _sFocus });
|
||||||
{
|
Assert.AreEqual(expected, actual);
|
||||||
ImageCropMode = ImageCropMode.Min,
|
|
||||||
Width = 300,
|
|
||||||
Height = 150,
|
|
||||||
});
|
|
||||||
var urlStringBoxPad =
|
|
||||||
s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
|
||||||
{
|
|
||||||
ImageCropMode = ImageCropMode.BoxPad,
|
|
||||||
Width = 300,
|
|
||||||
Height = 150,
|
|
||||||
});
|
|
||||||
var urlStringPad =
|
|
||||||
s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
|
||||||
{
|
|
||||||
ImageCropMode = ImageCropMode.Pad,
|
|
||||||
Width = 300,
|
|
||||||
Height = 150,
|
|
||||||
});
|
|
||||||
var urlStringMax =
|
|
||||||
s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
|
||||||
{
|
|
||||||
ImageCropMode = ImageCropMode.Max,
|
|
||||||
Width = 300,
|
|
||||||
Height = 150,
|
|
||||||
});
|
|
||||||
var urlStringStretch =
|
|
||||||
s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
|
||||||
{
|
|
||||||
ImageCropMode = ImageCropMode.Stretch,
|
|
||||||
Width = 300,
|
|
||||||
Height = 150,
|
|
||||||
});
|
|
||||||
|
|
||||||
Assert.AreEqual(MediaPath + "?rmode=min&width=300&height=150", urlStringMin);
|
|
||||||
Assert.AreEqual(MediaPath + "?rmode=boxpad&width=300&height=150", urlStringBoxPad);
|
|
||||||
Assert.AreEqual(MediaPath + "?rmode=pad&width=300&height=150", urlStringPad);
|
|
||||||
Assert.AreEqual(MediaPath + "?rmode=max&width=300&height=150", urlStringMax);
|
|
||||||
Assert.AreEqual(MediaPath + "?rmode=stretch&width=300&height=150", urlStringStretch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test for upload property type
|
/// Tests the correct query string is returned when given further options.
|
||||||
|
/// There are a few edge case inputs here to ensure thorough testing in future versions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[TestCase("&filter=comic&roundedcorners=radius-26%7Cbgcolor-fff", "?filter=comic&roundedcorners=radius-26%7Cbgcolor-fff")]
|
||||||
public void GetImageUrl_UploadTypeTest()
|
[TestCase("testoptions", "?testoptions=")]
|
||||||
|
[TestCase("&&&should=strip", "?should=strip")]
|
||||||
|
[TestCase("should=encode&$^%()", "?should=encode&$%5E%25()=")]
|
||||||
|
public void GivenFurtherOptions_ReturnsExpectedQueryString(string input, string expected)
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty)
|
||||||
{
|
{
|
||||||
ImageCropMode = ImageCropMode.Crop,
|
FurtherOptions = input,
|
||||||
ImageCropAnchor = ImageCropAnchor.Center,
|
|
||||||
Width = 100,
|
|
||||||
Height = 270,
|
|
||||||
});
|
});
|
||||||
Assert.AreEqual(MediaPath + "?rmode=crop&ranchor=center&width=100&height=270", urlString);
|
Assert.AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test for preferFocalPoint when focal point is centered
|
/// Test that the correct query string is returned for all image crop modes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[TestCase(ImageCropMode.Min, "?rmode=min")]
|
||||||
public void GetImageUrl_PreferFocalPointCenter()
|
[TestCase(ImageCropMode.BoxPad, "?rmode=boxpad")]
|
||||||
|
[TestCase(ImageCropMode.Pad, "?rmode=pad")]
|
||||||
|
[TestCase(ImageCropMode.Max, "?rmode=max")]
|
||||||
|
[TestCase(ImageCropMode.Stretch, "?rmode=stretch")]
|
||||||
|
public void GivenCropMode_ReturnsExpectedQueryString(ImageCropMode cropMode, string expectedQueryString)
|
||||||
{
|
{
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 300, Height = 150 });
|
var cropUrl = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty)
|
||||||
Assert.AreEqual(MediaPath + "?width=300&height=150", urlString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test to check if crop ratio is ignored if useCropDimensions is true
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void GetImageUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore()
|
|
||||||
{
|
|
||||||
var urlString =
|
|
||||||
s_generator.GetImageUrl(
|
|
||||||
new ImageUrlGenerationOptions(MediaPath) { FocalPoint = s_focus2, Width = 270, Height = 161 });
|
|
||||||
Assert.AreEqual(MediaPath + "?rxy=0.4275,0.41&width=270&height=161", urlString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test to check result when only a width parameter is passed, effectivly a resize only
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void GetImageUrl_WidthOnlyParameter()
|
|
||||||
{
|
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Width = 200 });
|
|
||||||
Assert.AreEqual(MediaPath + "?width=200", urlString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test to check result when only a height parameter is passed, effectivly a resize only
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void GetImageUrl_HeightOnlyParameter()
|
|
||||||
{
|
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Height = 200 });
|
|
||||||
Assert.AreEqual(MediaPath + "?height=200", urlString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test to check result when using a background color with padding
|
|
||||||
/// </summary>
|
|
||||||
[Test]
|
|
||||||
public void GetImageUrl_BackgroundColorParameter()
|
|
||||||
{
|
|
||||||
var urlString = s_generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
|
||||||
{
|
{
|
||||||
ImageCropMode = ImageCropMode.Pad,
|
ImageCropMode = cropMode,
|
||||||
Width = 400,
|
|
||||||
Height = 400,
|
|
||||||
FurtherOptions = "&bgcolor=fff",
|
|
||||||
});
|
});
|
||||||
Assert.AreEqual(MediaPath + "?rmode=pad&width=400&height=400&bgcolor=fff", urlString);
|
|
||||||
|
Assert.AreEqual(expectedQueryString, cropUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test that the correct query string is returned for all image crop anchors.
|
||||||
|
/// </summary>
|
||||||
|
[TestCase(ImageCropAnchor.Bottom, "?ranchor=bottom")]
|
||||||
|
[TestCase(ImageCropAnchor.BottomLeft, "?ranchor=bottomleft")]
|
||||||
|
[TestCase(ImageCropAnchor.BottomRight, "?ranchor=bottomright")]
|
||||||
|
[TestCase(ImageCropAnchor.Center, "?ranchor=center")]
|
||||||
|
[TestCase(ImageCropAnchor.Left, "?ranchor=left")]
|
||||||
|
[TestCase(ImageCropAnchor.Right, "?ranchor=right")]
|
||||||
|
[TestCase(ImageCropAnchor.Top, "?ranchor=top")]
|
||||||
|
[TestCase(ImageCropAnchor.TopLeft, "?ranchor=topleft")]
|
||||||
|
[TestCase(ImageCropAnchor.TopRight, "?ranchor=topright")]
|
||||||
|
public void GivenCropAnchor_ReturnsExpectedQueryString(ImageCropAnchor imageCropAnchor, string expectedQueryString)
|
||||||
|
{
|
||||||
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty)
|
||||||
|
{
|
||||||
|
ImageCropAnchor = imageCropAnchor,
|
||||||
|
});
|
||||||
|
Assert.AreEqual(expectedQueryString, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that the quality query string always returns the input number regardless of value.
|
||||||
|
/// </summary>
|
||||||
|
[TestCase(int.MinValue)]
|
||||||
|
[TestCase(-50)]
|
||||||
|
[TestCase(0)]
|
||||||
|
[TestCase(50)]
|
||||||
|
[TestCase(int.MaxValue)]
|
||||||
|
public void GivenQuality_ReturnsExpectedQueryString(int quality)
|
||||||
|
{
|
||||||
|
var expected = "?quality=" + quality;
|
||||||
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty)
|
||||||
|
{
|
||||||
|
Quality = quality,
|
||||||
|
});
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that the correct query string is returned for cache buster.
|
||||||
|
/// There are some edge case tests here to ensure thorough testing in future versions.
|
||||||
|
/// </summary>
|
||||||
|
[TestCase("test-buster", "?rnd=test-buster")]
|
||||||
|
[TestCase("test-buster&&^-value", "?rnd=test-buster%26%26%5E-value")]
|
||||||
|
public void GivenCacheBusterValue_ReturnsExpectedQueryString(string input, string expected)
|
||||||
|
{
|
||||||
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(string.Empty)
|
||||||
|
{
|
||||||
|
CacheBusterValue = input,
|
||||||
|
});
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that an expected query string is returned when all options are given.
|
||||||
|
/// This will be a good test to see if something breaks with ordering of query string parameters.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void GivenAllOptions_ReturnsExpectedQueryString()
|
||||||
|
{
|
||||||
|
const string expected =
|
||||||
|
"/media/1005/img_0671.jpg?cc=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&rxy=0.96,0.80827067669172936&rmode=stretch&ranchor=right&width=200&height=200&quality=50&more=options&rnd=buster";
|
||||||
|
|
||||||
|
var actual = _sGenerator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath)
|
||||||
|
{
|
||||||
|
Quality = 50,
|
||||||
|
Crop = _sCrop,
|
||||||
|
FocalPoint = _sFocus,
|
||||||
|
CacheBusterValue = "buster",
|
||||||
|
FurtherOptions = "more=options",
|
||||||
|
Height = 200,
|
||||||
|
Width = 200,
|
||||||
|
ImageCropAnchor = ImageCropAnchor.Right,
|
||||||
|
ImageCropMode = ImageCropMode.Stretch,
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.AreEqual(expected, actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user