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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deploy/wtDeployVersion.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.4.4
2.4.5
Binary file added release/Widgets Toolbox 2.4.5.mltbx
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<Info location="Widgets Toolbox 2.4.5.mltbx" type="File"/>
51 changes: 50 additions & 1 deletion test/+wt/+test/BaseTest.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
classdef BaseTest < matlab.uitest.TestCase
% Implements a unit test with some added helper methods

% Copyright 2020-2025 The MathWorks Inc.
% Copyright 2020-2026 The MathWorks Inc.


%% Protected Helper Methods
Expand Down Expand Up @@ -115,6 +115,55 @@ function verifyNotVisible(testCase, component)
end %function


function verifyEventuallyHasParent(testCase, component, parent)
% Verify the specified component eventually has a parent

arguments
testCase matlab.uitest.TestCase
component
parent matlab.graphics.Graphics {mustBeScalarOrEmpty} = gobjects(0)
end

import matlab.unittest.constraints.Eventually
import matlab.unittest.constraints.IsScalar
import matlab.unittest.constraints.IsEqualTo

% Verify values
if isempty(parent)
diag = "Expected the component to have a parent.";
testCase.verifyThat(...
@()get(component, "Parent"),...
Eventually(IsScalar, "WithTimeoutOf", 5), diag);
else
diag = "Expected the component to have the specified parent.";
testCase.verifyThat(...
@()get(component, "Parent"),...
Eventually(IsEqualTo(parent), "WithTimeoutOf", 5), diag);
end

end %function


function verifyEventuallyHasNoParent(testCase, component)
% Verify the specified component gets unparented

arguments
testCase matlab.uitest.TestCase
component
end

import matlab.unittest.constraints.Eventually
import matlab.unittest.constraints.IsEmpty

% Verify values
diag = "Expected the component to have its Parent be empty.";
testCase.verifyThat(...
@()get(component, "Parent"),...
Eventually(IsEmpty, "WithTimeoutOf", 5), diag);

end %function


function verifyEnabled(testCase, component)
% Verify the specified component is set to enabled

Expand Down
126 changes: 103 additions & 23 deletions test/+wt/+test/FileSelector.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
classdef FileSelector < wt.test.BaseWidgetTest
% Implements a unit test for a widget or component

% Copyright 2020-2025 The MathWorks Inc.
% Copyright 2020-2026 The MathWorks Inc.



Expand Down Expand Up @@ -170,6 +170,28 @@ function testFileEditField(testCase)
%testCase.verifyTrue(logical(testCase.Widget.WarnImage.Visible));

end %function


function testWebAddress(testCase)

% Get the edit field
editControl = testCase.Widget.EditControl;

% Set the type
testCase.verifySetProperty("SelectionType", "folder");

% Type a valid value
newValue = "s3://abucket/afolder";
testCase.verifyTypeAction(editControl, newValue, "Value");

% Verify the ValueIsValidPath value (false because not local
% folder)
testCase.verifyFalse(testCase.Widget.ValueIsValidPath)

% Verify the warn image does not show (we ignore web address)
testCase.verifyFalse(logical(testCase.Widget.WarnImage.Visible));

end %function


function testRootDirectoryAndHistory(testCase)
Expand Down Expand Up @@ -232,6 +254,7 @@ function testRootDirectoryAndHistory(testCase)

end %function


function testButtonLabel(testCase)

% Get the button control
Expand All @@ -251,6 +274,63 @@ function testButtonLabel(testCase)

end %function


function testButtonVisibility(testCase)

% Get the button control
buttonControl = testCase.Widget.ButtonControl;

% Sample paths to show
sampleFile = mfilename("fullpath");
sampleFolder = fileparts(sampleFile);


% --- SelectionType == folder --- %
testCase.Widget.SelectionType = wt.enum.FileFolderState.folder;
testCase.Widget.Value = sampleFolder;

% Normal App - should show button
testCase.Widget.IsWebApp = false;
testCase.verifyEventuallyHasParent(buttonControl);
testCase.Widget.forceUpdate();

% Web App - should NOT show button
testCase.Widget.IsWebApp = true;
testCase.verifyEventuallyHasNoParent(buttonControl); % NO Parent
testCase.Widget.forceUpdate();


% --- SelectionType == file --- %
testCase.Widget.SelectionType = wt.enum.FileFolderState.file;
testCase.Widget.Value = sampleFile;

% Normal App - should show button
testCase.Widget.IsWebApp = false;
testCase.verifyEventuallyHasParent(buttonControl);
testCase.Widget.forceUpdate();

% Web App - should show button
testCase.Widget.IsWebApp = true;
testCase.verifyEventuallyHasParent(buttonControl);
testCase.Widget.forceUpdate();


% --- SelectionType == putfile --- %
testCase.Widget.SelectionType = wt.enum.FileFolderState.putfile;
testCase.Widget.Value = sampleFile;

% Normal App - should show button
testCase.Widget.IsWebApp = false;
testCase.verifyEventuallyHasParent(buttonControl);
testCase.Widget.forceUpdate();

% Web App - should show button
testCase.Widget.IsWebApp = true;
testCase.verifyEventuallyHasParent(buttonControl);
testCase.Widget.forceUpdate();

end %function

% Since this test-case unlocks the test figure it should be last in
% line.
%RJ - Commented out the below test that fails in R2025a and later
Expand Down Expand Up @@ -306,25 +386,25 @@ function testButtonLabel(testCase)

end %classdef

function localPressEscape(fig)

% Unlock the figure, otherwise escape will not work.
matlab.uitest.unlock(fig);

% Bring focus to figure
figure(fig)

% Press ESCAPE
r = java.awt.Robot;
r.keyPress(java.awt.event.KeyEvent.VK_ESCAPE);
pause(0.1);
r.keyRelease(java.awt.event.KeyEvent.VK_ESCAPE);

end

function localRevertShowInWebAppsSetting(s, val)

% Revert setting on cleanup
s.matlab.ui.dialog.fileIO.ShowInWebApps.TemporaryValue = val;

end
% function localPressEscape(fig)
%
% % Unlock the figure, otherwise escape will not work.
% matlab.uitest.unlock(fig);
%
% % Bring focus to figure
% figure(fig)
%
% % Press ESCAPE
% r = java.awt.Robot;
% r.keyPress(java.awt.event.KeyEvent.VK_ESCAPE);
% pause(0.1);
% r.keyRelease(java.awt.event.KeyEvent.VK_ESCAPE);
%
% end
%
% function localRevertShowInWebAppsSetting(s, val)
%
% % Revert setting on cleanup
% s.matlab.ui.dialog.fileIO.ShowInWebApps.TemporaryValue = val;
%
% end
63 changes: 54 additions & 9 deletions widgets/+wt/+utility/cleanPath.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function path = cleanPath(path)
function out = cleanPath(in)
% cleanPath - Utility to clean and standardize a file/folder path
%
% This function will clean and standardize a file or folder path. It
Expand All @@ -24,15 +24,60 @@
% "C:\Program Files\MATLAB"
%

% Copyright 2020-2025 The MathWorks Inc.
% Copyright 2020-2026 The MathWorks Inc.
% ---------------------------------------------------------------------

% File separator - in case of regional variants
fsep = regexptranslate("escape",filesep);
arguments
in string % accepts string arrays of any size
end

% Pattern for regexp
fsepOpts = join(["\\","/",fsep],"|");
pattern = "^\s+|(?<=\S)(\s|" + fsepOpts + ")+$";
if isempty(in)
out = in;
return
end

% Perform replacement
path = regexprep(path,pattern,"");
fs = filesep;
sz = size(in);
out = strings(sz); % allocate same size

for idx = 1:numel(in)
sIn = char(in(idx));
if strlength(in(idx)) == 0
out(idx) = in(idx);
continue
end

% If URI-like (scheme://) then leave unchanged
if ~isempty(regexp(sIn, '^[A-Za-z][A-Za-z0-9+.\-]*://', 'once'))
out(idx) = in(idx);
continue
end

% Detect UNC-style leading slashes
isUNC = ~isempty(regexp(sIn, '^[\\/]{2,}', 'once'));

% Collapse any run of slashes/backslashes to single platform filesep
s = regexprep(sIn, '[\\/]+', fs);

if isUNC
% Remove any leading separators then ensure exactly two leading filesep
s = regexprep(s, ['^' regexptranslate('escape', fs) '+'], '');
s = [repmat(fs,1,2) s];
else
% Preserve drive-letter prefix like 'C:' and ensure at most one filesep after it
m = regexp(sIn, '^([A-Za-z]:)[\\/]*', 'tokens', 'once');
if ~isempty(m)
drive = m{1};
% Strip any leading drive+sep from s, then reapply drive and single filesep if needed
s = regexprep(s, ['^' regexptranslate('escape', drive) regexptranslate('escape', fs) '?'], '');
if isempty(s)
s = drive;
else
s = [drive fs s];
end
end
end

out(idx) = string(s);
end
end
Loading
Loading