Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
5c31ad8851 | |||
3ac9536e76 | |||
7246852181 | |||
dd48a24c58 | |||
ffef1fd6e4 | |||
01d658f51d | |||
5a36e84b1f | |||
5b2f617a68 | |||
dd5049faae | |||
ffe0ff5508 | |||
34178105a7 | |||
6b48a1090c | |||
ddd821e478 | |||
658a1f4dc1 | |||
daf83c4bbc | |||
26d75ea3cd | |||
86937485e4 | |||
e9de54415a | |||
62f63ef63d | |||
44656e0022 |
@ -1,6 +1,7 @@
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
branches: ["**"]
|
||||
paths: ["Elwig/**", "Tests/**", "Installer/Files/*.exe"]
|
||||
jobs:
|
||||
test:
|
||||
@ -26,4 +27,4 @@ jobs:
|
||||
shell: powershell
|
||||
run: |
|
||||
$env:PATH = "$(pwd)\Installer\Files;" + $env:PATH
|
||||
$(& dotnet test Tests; $a=$lastexitcode) | findstr x*; exit $a
|
||||
$(& dotnet test Tests --filter "FullyQualifiedName!~E2ETests"; $a=$lastexitcode) | findstr x*; exit $a
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ bin/
|
||||
Tests/Resources/Sql/Create.sql
|
||||
*.exe
|
||||
!WinziPrint.exe
|
||||
*.sqlite3
|
||||
|
22
CHANGELOG.md
22
CHANGELOG.md
@ -3,6 +3,28 @@ Changelog
|
||||
=========
|
||||
|
||||
|
||||
[v0.8.7][v0.8.7] (2024-07-22) {#v0.8.7}
|
||||
---------------------------------------
|
||||
|
||||
### Neue Funktionen {#v0.8.7-features}
|
||||
|
||||
* Im Mitglieder-Fenster (`MemberAdminWindow`) muss eine E-Mail-Adresse angegeben werden, wenn E-Mail als Kontaktart angegeben wird. (5a36e84b1f)
|
||||
|
||||
### Sonstiges {#v0.8.7-misc}
|
||||
|
||||
* Automatisierte Ende-zu-Ende-Tests (`E2ETests`) hinzugefügt. (ddd821e478, 6b48a1090c, dd5049faae, 5b2f617a68, 7246852181)
|
||||
* Automatisierte Tests überarbeitet. (44656e0022, 86937485e4, 34178105a7, ffe0ff5508, 01d658f51d)
|
||||
* Update-Dialog (`UpdateDialog`) überarbeitet. (62f63ef63d, e9de54415a)
|
||||
* Eingebefelder vom Typ _Mehrfachauswahl mit Dropdown_ (`CheckComboBox`) etwas verbessert. (26d75ea3cd, daf83c4bbc)
|
||||
* Im Übernahme-Fenster (`DeliveryAdminWindow`) werden nur noch aktive Mitglieder angezeigt. (658a1f4dc1)
|
||||
* Abhängigkeiten aktualisiert. (ffef1fd6e4, dd48a24c58)
|
||||
* Auto-Update-Funktion überarbeitet. (3ac9536e76)
|
||||
|
||||
[v0.8.7]: https://git.necronda.net/winzer/elwig/releases/tag/v0.8.7
|
||||
|
||||
|
||||
|
||||
|
||||
[v0.8.6][v0.8.6] (2024-07-01) {#v0.8.6}
|
||||
---------------------------------------
|
||||
|
||||
|
@ -31,8 +31,8 @@ namespace Elwig {
|
||||
public static readonly string DataPath = @"C:\ProgramData\Elwig\";
|
||||
public static readonly string ExePath = @"C:\Program Files\Elwig\";
|
||||
public static readonly string TempPath = Path.Combine(Path.GetTempPath(), "Elwig");
|
||||
public static readonly Config Config = new(DataPath + "config.ini");
|
||||
|
||||
public static Config Config { get; private set; } = new(Path.Combine(DataPath, "config.ini"));
|
||||
public static int VersionMajor { get; private set; }
|
||||
public static int VersionMinor { get; private set; }
|
||||
public static int VersionPatch { get; private set; }
|
||||
@ -74,6 +74,11 @@ namespace Elwig {
|
||||
CurrentApp = this;
|
||||
OverrideCulture();
|
||||
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
if (args.Length >= 2) {
|
||||
Config = new(Path.GetFullPath(args[1]));
|
||||
}
|
||||
|
||||
ContextTimer.Tick += (object? sender, EventArgs evt) => {
|
||||
if (CurrentLastWrite > LastChanged) {
|
||||
LastChanged = CurrentLastWrite;
|
||||
@ -159,7 +164,7 @@ namespace Elwig {
|
||||
list.Add(Scale.FromConfig(s));
|
||||
} catch (Exception e) {
|
||||
list.Add(new InvalidScale(s.Id));
|
||||
if (Config.Debug || s.Required)
|
||||
if (s.Required)
|
||||
MessageBox.Show($"Unable to create scale {s.Id}:\n\n{e.Message}", "Scale Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
@ -222,7 +227,7 @@ namespace Elwig {
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task CheckForUpdates(bool showSuccess = false) {
|
||||
public static async Task CheckForUpdates(bool showAlert = false) {
|
||||
if (Config.UpdateUrl == null) return;
|
||||
var latest = await Utils.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||
if (latest != null && new Version(latest.Value.Version) > new Version(Version)) {
|
||||
@ -233,9 +238,14 @@ namespace Elwig {
|
||||
Current.Shutdown();
|
||||
}
|
||||
});
|
||||
} else if (showSuccess) {
|
||||
MessageBox.Show("Elwig ist auf dem aktuellsten Stand!", "Nach Updates suchen",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
} else if (showAlert) {
|
||||
if (latest == null) {
|
||||
MessageBox.Show("Informationen konnten nicht abgerufen werden!", "Nach Updates suchen",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
} else {
|
||||
MessageBox.Show($"Elwig ist auf dem aktuellsten Stand! (Version: {latest.Value.Version})", "Nach Updates suchen",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,73 +1,105 @@
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Elwig.Controls {
|
||||
public class CheckComboBox : ListBox {
|
||||
|
||||
public static readonly DependencyProperty DelimiterProperty = DependencyProperty.Register("Delimiter", typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata(", "));
|
||||
public new static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(nameof(SelectedItems), typeof(IList), typeof(CheckComboBox), new FrameworkPropertyMetadata(new ObservableCollection<object>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemsChangedCallback));
|
||||
public new IList SelectedItems {
|
||||
get => (IList)GetValue(SelectedItemsProperty);
|
||||
set => SetValue(SelectedItemsProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DelimiterProperty = DependencyProperty.Register(nameof(Delimiter), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata(", "));
|
||||
public string Delimiter {
|
||||
get => (string)GetValue(DelimiterProperty);
|
||||
set => SetValue(DelimiterProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty AllItemsSelectedContentProperty = DependencyProperty.Register("AllItemsSelectedContent", typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
||||
public static readonly DependencyProperty AllItemsSelectedContentProperty = DependencyProperty.Register(nameof(AllItemsSelectedContent), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
||||
public string AllItemsSelectedContent {
|
||||
get => (string)GetValue(AllItemsSelectedContentProperty);
|
||||
set => SetValue(AllItemsSelectedContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsSelectAllActiveProperty = DependencyProperty.Register("IsSelectAllActive", typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsSelectAllActiveProperty = DependencyProperty.Register(nameof(IsSelectAllActive), typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||
public bool IsSelectAllActive {
|
||||
get => (bool)GetValue(IsSelectAllActiveProperty);
|
||||
set => SetValue(IsSelectAllActiveProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SelectAllContentProperty = DependencyProperty.Register("SelectAllContent", typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
||||
public static readonly DependencyProperty SelectAllContentProperty = DependencyProperty.Register(nameof(SelectAllContent), typeof(string), typeof(CheckComboBox), new FrameworkPropertyMetadata("All"));
|
||||
public string SelectAllContent {
|
||||
get => (string)GetValue(SelectAllContentProperty);
|
||||
set => SetValue(SelectAllContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty AllItemsSelectedProperty = DependencyProperty.Register("AllItemsSelected", typeof(bool?), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||
public static readonly DependencyProperty AllItemsSelectedProperty = DependencyProperty.Register(nameof(AllItemsSelected), typeof(bool?), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||
public bool? AllItemsSelected {
|
||||
get => (bool?)GetValue(AllItemsSelectedProperty);
|
||||
set => SetValue(AllItemsSelectedProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register("IsDropDownOpen", typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(nameof(IsDropDownOpen), typeof(bool), typeof(CheckComboBox), new FrameworkPropertyMetadata(false));
|
||||
public bool IsDropDownOpen {
|
||||
get => (bool)GetValue(IsDropDownOpenProperty);
|
||||
set => SetValue(IsDropDownOpenProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register("MaxDropDownHeight", typeof(double), typeof(CheckComboBox), new FrameworkPropertyMetadata(ComboBox.MaxDropDownHeightProperty.DefaultMetadata.DefaultValue));
|
||||
public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(nameof(MaxDropDownHeight), typeof(double), typeof(CheckComboBox), new FrameworkPropertyMetadata(ComboBox.MaxDropDownHeightProperty.DefaultMetadata.DefaultValue));
|
||||
public double MaxDropDownHeight {
|
||||
get => (double)GetValue(MaxDropDownHeightProperty);
|
||||
set => SetValue(MaxDropDownHeightProperty, value);
|
||||
}
|
||||
|
||||
public new static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent(nameof(SelectionChanged), RoutingStrategy.Bubble, typeof(SelectionChangedEventHandler), typeof(CheckComboBox));
|
||||
public new event SelectionChangedEventHandler SelectionChanged {
|
||||
add => AddHandler(SelectionChangedEvent, value);
|
||||
remove => RemoveHandler(SelectionChangedEvent, value);
|
||||
}
|
||||
|
||||
static CheckComboBox() {
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckComboBox), new FrameworkPropertyMetadata(typeof(CheckComboBox)));
|
||||
}
|
||||
|
||||
private TextBlock TextBox;
|
||||
private bool _viewHandled;
|
||||
private bool _modelHandled;
|
||||
private TextBlock _textBox;
|
||||
|
||||
public CheckComboBox() {
|
||||
SelectionMode = SelectionMode.Multiple;
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate() {
|
||||
TextBox = (GetTemplateChild("TextBox") as TextBlock)!;
|
||||
_textBox = (GetTemplateChild("TextBox") as TextBlock)!;
|
||||
var button = GetTemplateChild("Button") as Button;
|
||||
button!.Click += Button_MouseDown;
|
||||
var item = GetTemplateChild("SelectAllItem") as ListBoxItem;
|
||||
item!.PreviewMouseDown += SelectAllItem_MouseDown;
|
||||
SelectionChanged += OnSelectionChanged;
|
||||
if (SelectedItems is INotifyCollectionChanged collection) {
|
||||
collection.CollectionChanged += (s, e) => { SelectItems(); };
|
||||
}
|
||||
IsEnabledChanged += OnIsEnabledChanged;
|
||||
base.SelectionChanged += OnSelectionChanged;
|
||||
base.OnApplyTemplate();
|
||||
}
|
||||
|
||||
private static void OnSelectedItemsChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e) {
|
||||
if (sender is CheckComboBox ccb)
|
||||
ccb.OnSelectedItemsChanged();
|
||||
}
|
||||
|
||||
private void OnSelectedItemsChanged() {
|
||||
if (SelectedItems is INotifyCollectionChanged collection) {
|
||||
collection.CollectionChanged += (s, e) => { SelectItems(); };
|
||||
}
|
||||
SelectItems();
|
||||
}
|
||||
|
||||
private void Button_MouseDown(object sender, RoutedEventArgs evt) {
|
||||
IsDropDownOpen = !IsDropDownOpen;
|
||||
}
|
||||
@ -82,25 +114,47 @@ namespace Elwig.Controls {
|
||||
}
|
||||
|
||||
private void OnSelectionChanged(object sender, SelectionChangedEventArgs evt) {
|
||||
SelectItemsReverse();
|
||||
var dmp = DisplayMemberPath != null && DisplayMemberPath != "" ? DisplayMemberPath : null;
|
||||
if (SelectedItems.Count == ItemsSource.Cast<object>().Count() && AllItemsSelectedContent != null) {
|
||||
TextBox.Text = AllItemsSelectedContent;
|
||||
_textBox.Text = AllItemsSelectedContent;
|
||||
AllItemsSelected = true;
|
||||
} else if (SelectedItems.Count == 0) {
|
||||
TextBox.Text = "";
|
||||
_textBox.Text = "";
|
||||
AllItemsSelected = false;
|
||||
} else {
|
||||
TextBox.Text = string.Join(Delimiter,
|
||||
_textBox.Text = string.Join(Delimiter,
|
||||
dmp == null ? SelectedItems.Cast<object>() :
|
||||
SelectedItems.Cast<object>()
|
||||
.Select(i => i.GetType().GetProperty(dmp)?.GetValue(i))
|
||||
);
|
||||
AllItemsSelected = null;
|
||||
}
|
||||
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, evt.RemovedItems, evt.AddedItems));
|
||||
}
|
||||
|
||||
private void OnIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs evt) {
|
||||
if (!IsEnabled) IsDropDownOpen = false;
|
||||
}
|
||||
|
||||
private void SelectItems() {
|
||||
if (_viewHandled || _modelHandled)
|
||||
return;
|
||||
_viewHandled = true;
|
||||
base.SelectedItems.Clear();
|
||||
foreach (var item in SelectedItems)
|
||||
base.SelectedItems.Add(item);
|
||||
_viewHandled = false;
|
||||
}
|
||||
|
||||
private void SelectItemsReverse() {
|
||||
if (_modelHandled || _viewHandled)
|
||||
return;
|
||||
_modelHandled = true;
|
||||
SelectedItems.Clear();
|
||||
foreach (var item in base.SelectedItems)
|
||||
SelectedItems.Add(item);
|
||||
_modelHandled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,13 @@ using System.Windows.Input;
|
||||
namespace Elwig.Controls {
|
||||
public class IntegerUpDown : TextBox {
|
||||
|
||||
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Miminum", typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
||||
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(nameof(Minimum), typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
||||
public int? Minimum {
|
||||
get => (int?)GetValue(MinimumProperty);
|
||||
set => SetValue(MinimumProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
||||
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(nameof(Maximum), typeof(int?), typeof(IntegerUpDown), new FrameworkPropertyMetadata(null));
|
||||
public int? Maximum {
|
||||
get => (int?)GetValue(MaximumProperty);
|
||||
set => SetValue(MaximumProperty, value);
|
||||
|
@ -6,13 +6,13 @@ using System.Windows.Data;
|
||||
namespace Elwig.Controls {
|
||||
public class UnitConverter : DependencyObject, IValueConverter {
|
||||
|
||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register("Unit", typeof(string), typeof(UnitConverter), new FrameworkPropertyMetadata(null));
|
||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register(nameof(Unit), typeof(string), typeof(UnitConverter), new FrameworkPropertyMetadata(null));
|
||||
public string Unit {
|
||||
get => (string)GetValue(UnitProperty);
|
||||
set => SetValue(UnitProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register("Precision", typeof(byte), typeof(UnitConverter), new FrameworkPropertyMetadata((byte)0));
|
||||
public static readonly DependencyProperty PrecisionProperty = DependencyProperty.Register(nameof(Precision), typeof(byte), typeof(UnitConverter), new FrameworkPropertyMetadata((byte)0));
|
||||
public byte Precision {
|
||||
get => (byte)GetValue(PrecisionProperty);
|
||||
set => SetValue(PrecisionProperty, value);
|
||||
|
@ -4,7 +4,7 @@ using System.Windows.Controls;
|
||||
namespace Elwig.Controls {
|
||||
public class UnitTextBox : TextBox {
|
||||
|
||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register("Unit", typeof(string), typeof(UnitTextBox), new FrameworkPropertyMetadata(""));
|
||||
public static readonly DependencyProperty UnitProperty = DependencyProperty.Register(nameof(Unit), typeof(string), typeof(UnitTextBox), new FrameworkPropertyMetadata(""));
|
||||
public string Unit {
|
||||
get => (string)GetValue(UnitProperty);
|
||||
set => SetValue(UnitProperty, value);
|
||||
|
@ -12,4 +12,4 @@ namespace Elwig.Controls {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<Window x:Class="Elwig.Dialogs.DeleteMemberDialog"
|
||||
AutomationProperties.AutomationId="DeleteMemberDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
|
@ -1,4 +1,5 @@
|
||||
<Window x:Class="Elwig.Dialogs.NewSeasonDialog"
|
||||
AutomationProperties.AutomationId="NewSeasonDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
|
@ -5,11 +5,13 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Title="Neues Update verfügbar - Elwig" Height="180" Width="400">
|
||||
Title="Neues Update verfügbar - Elwig" Height="190" Width="400"
|
||||
Closed="OnClosed">
|
||||
<Grid>
|
||||
<TextBlock x:Name="Description" FontSize="14" Margin="0,0,0,30"
|
||||
<TextBlock x:Name="Description" FontSize="14" Margin="0,0,0,40"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center">
|
||||
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> von Elwig ist verfügbar!<LineBreak/>
|
||||
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> von Elwig ist verfügbar!
|
||||
(<Hyperlink NavigateUri="https://elwig.at/changelog" RequestNavigate="Hyperlink_RequestNavigate">Änderungen</Hyperlink>)<LineBreak/>
|
||||
Soll das Update heruntergeladen und<LineBreak/>
|
||||
installiert werden? (ca. <Run x:Name="SizeText">100</Run> MB)<LineBreak/>
|
||||
<Run FontWeight="Bold">Achtung</Run>: Elwig wird dabei geschlossen!
|
||||
@ -19,12 +21,12 @@
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Height="27" Width="300" SnapsToDevicePixels="True"/>
|
||||
|
||||
<Button x:Name="InstallButton" Content="Installieren" Margin="10,10,115,10"
|
||||
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
<Button x:Name="InstallButton" Content="Installieren" Margin="10,10,115,20"
|
||||
FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||
Width="100" Height="27"
|
||||
Click="InstallButton_Click"/>
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="10,10,10,10" IsCancel="True" IsDefault="True"
|
||||
FontSize="14" HorizontalAlignment="Right" VerticalAlignment="Bottom"
|
||||
<Button x:Name="CancelButton" Content="Abbrechen" Margin="115,10,10,20" IsCancel="True"
|
||||
FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
||||
Width="100" Height="27"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
@ -3,8 +3,10 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace Elwig.Dialogs {
|
||||
public partial class UpdateDialog : Window {
|
||||
@ -12,36 +14,54 @@ namespace Elwig.Dialogs {
|
||||
public string Version { get; private set; }
|
||||
public string Url { get; private set; }
|
||||
|
||||
private readonly CancellationTokenSource Cancellation;
|
||||
|
||||
public UpdateDialog(string version, string url, long size) {
|
||||
Version = version;
|
||||
Url = url;
|
||||
Cancellation = new();
|
||||
InitializeComponent();
|
||||
VersionText.Text = version;
|
||||
SizeText.Text = $"{size / 1024 / 1024}";
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, EventArgs evt) {
|
||||
Cancellation.Cancel();
|
||||
}
|
||||
|
||||
private async void InstallButton_Click(object sender, RoutedEventArgs evt) {
|
||||
Description.Visibility = Visibility.Hidden;
|
||||
ProgressBar.Visibility = Visibility.Visible;
|
||||
InstallButton.IsEnabled = false;
|
||||
await Install();
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
public async Task Install() {
|
||||
var fileName = Path.Combine(App.TempPath, $"Elwig-{Version}.exe");
|
||||
{
|
||||
try {
|
||||
using var stream = new FileStream(fileName, FileMode.Create);
|
||||
using var client = new HttpClient() {
|
||||
Timeout = TimeSpan.FromSeconds(5),
|
||||
};
|
||||
await client.DownloadAsync(Url, stream, new Progress<double>(p => {
|
||||
ProgressBar.Value = p * 100.0;
|
||||
}));
|
||||
}), Cancellation.Token);
|
||||
} catch (OperationCanceledException) {
|
||||
File.Delete(fileName);
|
||||
return;
|
||||
} catch (Exception exc) {
|
||||
MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
Process.Start(fileName);
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) {
|
||||
Process.Start(new ProcessStartInfo {
|
||||
FileName = e.Uri.ToString(),
|
||||
UseShellExecute = true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
<UseWPF>true</UseWPF>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<ApplicationIcon>Resources\Images\Elwig.ico</ApplicationIcon>
|
||||
<Version>0.8.6</Version>
|
||||
<Version>0.8.7</Version>
|
||||
<SatelliteResourceLanguages>de-AT</SatelliteResourceLanguages>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
@ -25,16 +25,16 @@
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LinqKit" Version="1.2.5" />
|
||||
<PackageReference Include="MailKit" Version="4.6.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.31" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||
<PackageReference Include="LinqKit" Version="1.3.0" />
|
||||
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.32" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2535.41" />
|
||||
<PackageReference Include="NJsonSchema" Version="11.0.0" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
|
||||
<PackageReference Include="NJsonSchema" Version="11.0.2" />
|
||||
<PackageReference Include="RazorLight" Version="2.3.1" />
|
||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.34" />
|
||||
<PackageReference Include="ScottPlot.WPF" Version="5.0.36" />
|
||||
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
@ -98,15 +98,15 @@ namespace Elwig.Helpers {
|
||||
SavedChanges += OnSavedChanges;
|
||||
}
|
||||
|
||||
public static SqliteConnection Connect() {
|
||||
var cnx = new SqliteConnection(ConnectionString);
|
||||
public static SqliteConnection Connect(string? connectionString = null) {
|
||||
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||
cnx.Open();
|
||||
return cnx;
|
||||
}
|
||||
|
||||
public static async Task<SqliteConnection> ConnectAsync() {
|
||||
var cnx = new SqliteConnection(ConnectionString);
|
||||
public static async Task<SqliteConnection> ConnectAsync(string? connectionString = null) {
|
||||
var cnx = new SqliteConnection(connectionString ?? ConnectionString);
|
||||
cnx.CreateFunction<string, string?, bool?>("REGEXP", (pattern, value) => value == null ? null : Regex.Match(value, pattern).Success, true);
|
||||
await cnx.OpenAsync();
|
||||
return cnx;
|
||||
|
@ -9,7 +9,7 @@ namespace Elwig.Helpers {
|
||||
public static class AppDbUpdater {
|
||||
|
||||
// Don't forget to update value in Tests/fetch-resources.bat!
|
||||
public static readonly int RequiredSchemaVersion = 23;
|
||||
public static readonly int RequiredSchemaVersion = 24;
|
||||
|
||||
private static int VersionOffset = 0;
|
||||
|
||||
@ -17,7 +17,7 @@ namespace Elwig.Helpers {
|
||||
using var cnx = AppDbContext.Connect();
|
||||
|
||||
var applId = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA application_id") ?? 0;
|
||||
if (applId != 0x454C5747) throw new Exception("Invalid application_id of database");
|
||||
if (applId != 0x454C5747) throw new Exception($"Invalid application_id in database (0x{applId:X08})");
|
||||
|
||||
var schemaVers = (long?)await AppDbContext.ExecuteScalar(cnx, "PRAGMA schema_version") ?? 0;
|
||||
VersionOffset = (int)(schemaVers % 100);
|
||||
|
@ -69,9 +69,9 @@ namespace Elwig.Helpers {
|
||||
public void Read() {
|
||||
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
||||
|
||||
DatabaseFile = Path.Combine(App.DataPath, config["database:file"] ?? "database.sqlite3");
|
||||
DatabaseFile = Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, config["database:file"] ?? "database.sqlite3");
|
||||
var log = config["database:log"];
|
||||
DatabaseLog = log != null ? Path.Combine(App.DataPath, log) : null;
|
||||
DatabaseLog = log != null ? Path.Combine(Path.GetDirectoryName(FileName) ?? App.DataPath, log) : null;
|
||||
Branch = config["general:branch"];
|
||||
Debug = TrueValues.Contains(config["general:debug"]?.ToLower());
|
||||
UpdateUrl = config["update:url"];
|
||||
|
@ -425,8 +425,11 @@ namespace Elwig.Helpers {
|
||||
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string url) {
|
||||
try {
|
||||
using var client = GetHttpClient(accept: "application/json");
|
||||
var resJson = JsonNode.Parse(await client.GetStringAsync(url));
|
||||
var data = resJson!["data"]![0]!;
|
||||
using var res = await client.GetAsync(url);
|
||||
if (!res.IsSuccessStatusCode)
|
||||
return null;
|
||||
var resJson = JsonNode.Parse(await res.Content.ReadAsStringAsync());
|
||||
var data = resJson!["data"]!.AsArray()[^1]!;
|
||||
return ((string)data["version"]!, (string)data["url"]!, (long)data["size"]!);
|
||||
} catch {
|
||||
return null;
|
||||
|
@ -13,6 +13,9 @@ namespace Elwig.Models.Entities {
|
||||
[Column("modid")]
|
||||
public required string ModId { get; set; }
|
||||
|
||||
[Column("active")]
|
||||
public bool IsActive { get; set; }
|
||||
|
||||
[Column("ordering")]
|
||||
public int Ordering { get; set; }
|
||||
|
||||
@ -21,7 +24,6 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[Column("abs")]
|
||||
public long? AbsValue { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public decimal? Abs {
|
||||
get => AbsValue != null ? Season.DecFromDb(AbsValue.Value) : null;
|
||||
@ -30,19 +32,12 @@ namespace Elwig.Models.Entities {
|
||||
|
||||
[Column("rel")]
|
||||
public double? RelValue { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public decimal? Rel {
|
||||
get => (decimal?)RelValue;
|
||||
set => RelValue = (double?)value;
|
||||
}
|
||||
|
||||
[Column("standard")]
|
||||
public bool IsStandard { get; set; }
|
||||
|
||||
[Column("quick_select")]
|
||||
public bool IsQuickSelect { get; set; }
|
||||
|
||||
[ForeignKey("Year")]
|
||||
public virtual Season Season { get; private set; } = null!;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- schema version 20 to 21
|
||||
-- schema version 21 to 22
|
||||
|
||||
CREATE VIEW v_penalty_business_shares AS
|
||||
SELECT u.year, u.mgnr,
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- schema version 20 to 21
|
||||
-- schema version 22 to 23
|
||||
|
||||
CREATE VIEW v_stat_modifier AS
|
||||
SELECT v.year, v.avnr, m.modid, m.name, m.abs, m.rel,
|
||||
|
5
Elwig/Resources/Sql/23-24.sql
Normal file
5
Elwig/Resources/Sql/23-24.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- schema version 23 to 24
|
||||
|
||||
ALTER TABLE modifier DROP COLUMN standard;
|
||||
ALTER TABLE modifier DROP COLUMN quick_select;
|
||||
ALTER TABLE modifier ADD COLUMN active INTEGER NOT NULL CHECK (active IN (TRUE, FALSE)) DEFAULT TRUE;
|
@ -1,5 +1,6 @@
|
||||
<local:AdministrationWindow
|
||||
x:Class="Elwig.Windows.BaseDataWindow"
|
||||
AutomationProperties.AutomationId="BaseDataWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@ -521,6 +522,10 @@
|
||||
<Label Content="Absolut:" Grid.Column="1" Margin="10,100,10,10"/>
|
||||
<ctrl:UnitTextBox x:Name="SeasonModifierAbsInput" Unit="€/kg" TextChanged="SeasonModifierAbsInput_TextChanged"
|
||||
Grid.Column="2" Width="86" Margin="0,100,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"/>
|
||||
|
||||
<CheckBox x:Name="SeasonModifierActiveInput" Content="In Übernahme-Fenster anzeigen"
|
||||
Grid.Column="1" Grid.ColumnSpan="2" Margin="10,134,10,10" HorizontalAlignment="Left" VerticalAlignment="Top"
|
||||
Checked="SeasonModifier_Changed" Unchecked="SeasonModifier_Changed"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
@ -134,11 +134,13 @@ namespace Elwig.Windows {
|
||||
SeasonModifierNameInput.Text = "";
|
||||
SeasonModifierRelInput.Text = "";
|
||||
SeasonModifierAbsInput.Text = "";
|
||||
SeasonModifierActiveInput.IsChecked = false;
|
||||
} else {
|
||||
SeasonModifierIdInput.Text = mod.ModId;
|
||||
SeasonModifierNameInput.Text = mod.Name;
|
||||
SeasonModifierRelInput.Text = (mod.Rel * 100)?.ToString() ?? "";
|
||||
SeasonModifierAbsInput.Text = mod.Abs?.ToString() ?? "";
|
||||
SeasonModifierActiveInput.IsChecked = mod.IsActive;
|
||||
}
|
||||
_modUpdate = false;
|
||||
}
|
||||
@ -154,6 +156,7 @@ namespace Elwig.Windows {
|
||||
mod.Name = SeasonModifierNameInput.Text;
|
||||
mod.Rel = decimal.TryParse(SeasonModifierRelInput.Text, out var vRel) ? vRel / 100 : null;
|
||||
mod.AbsValue = decimal.TryParse(SeasonModifierAbsInput.Text, out var vAbs) ? Utils.DecToDb(vAbs, s.Precision) : null;
|
||||
mod.IsActive = SeasonModifierActiveInput.IsChecked ?? false;
|
||||
|
||||
CollectionViewSource.GetDefaultView(_modList).Refresh();
|
||||
UpdateButtons();
|
||||
|
@ -187,8 +187,7 @@ namespace Elwig.Windows {
|
||||
Name = m.Name,
|
||||
AbsValue = m.AbsValue * mult / div,
|
||||
RelValue = m.RelValue,
|
||||
IsStandard = m.IsStandard,
|
||||
IsQuickSelect = m.IsQuickSelect,
|
||||
IsActive = m.IsActive,
|
||||
}));
|
||||
}
|
||||
await ctx.SaveChangesAsync();
|
||||
|
@ -1,4 +1,6 @@
|
||||
<local:AdministrationWindow x:Class="Elwig.Windows.DeliveryAdminWindow"
|
||||
<local:AdministrationWindow
|
||||
x:Class="Elwig.Windows.DeliveryAdminWindow"
|
||||
AutomationProperties.AutomationId="DeliveryAdminWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
|
@ -1084,7 +1084,7 @@ namespace Elwig.Windows {
|
||||
ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
|
||||
ControlUtils.RenewItemsSource(WineQualityLevelInput, await ctx.WineQualityLevels.ToListAsync());
|
||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||
.Where(m => m.Year == y)
|
||||
.Where(m => m.Year == y && (!IsCreating || m.IsActive))
|
||||
.OrderBy(m => m.Ordering)
|
||||
.Include(m => m.Season.Currency)
|
||||
.ToListAsync());
|
||||
@ -1116,14 +1116,14 @@ namespace Elwig.Windows {
|
||||
using var ctx = new AppDbContext();
|
||||
if (DeliveryList.SelectedItem is Delivery d) {
|
||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||
.Where(m => m.Year == d.Year)
|
||||
.Where(m => m.Year == d.Year && (!IsCreating || m.IsActive))
|
||||
.OrderBy(m => m.Ordering)
|
||||
.Include(m => m.Season.Currency)
|
||||
.ToListAsync());
|
||||
ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
|
||||
} else {
|
||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||
.Where(m => m.Year == SeasonInput.Value)
|
||||
.Where(m => m.Year == SeasonInput.Value && (!IsCreating || m.IsActive))
|
||||
.OrderBy(m => m.Ordering)
|
||||
.Include(m => m.Season.Currency)
|
||||
.ToListAsync());
|
||||
@ -1555,6 +1555,11 @@ namespace Elwig.Windows {
|
||||
var attrList = await ctx.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
|
||||
attrList.Insert(0, new NullItem(""));
|
||||
ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
|
||||
ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
|
||||
.Where(m => m.Year == SeasonInput.Value && m.IsActive)
|
||||
.OrderBy(m => m.Ordering)
|
||||
.Include(m => m.Season.Currency)
|
||||
.ToListAsync());
|
||||
ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
|
||||
.Where(m => m.IsActive || !IsReceipt)
|
||||
.Include(m => m.PostalDest.AtPlz!.Ort)
|
||||
|
@ -1,4 +1,5 @@
|
||||
<Window x:Class="Elwig.Windows.DocumentViewerWindow"
|
||||
AutomationProperties.AutomationId="DocumentViewerWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
|
@ -60,31 +60,31 @@ namespace Elwig.Windows {
|
||||
protected Document? PrintDocument;
|
||||
protected Dictionary<Member, List<Document>>? EmailDocuments;
|
||||
|
||||
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register("PostalAllCount", typeof(int), typeof(MailWindow));
|
||||
public static readonly DependencyProperty PostalAllCountProperty = DependencyProperty.Register(nameof(PostalAllCount), typeof(int), typeof(MailWindow));
|
||||
public int PostalAllCount {
|
||||
get => (int)GetValue(PostalAllCountProperty);
|
||||
private set => SetValue(PostalAllCountProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PostalWishCountProperty = DependencyProperty.Register("PostalWishCount", typeof(int), typeof(MailWindow));
|
||||
public static readonly DependencyProperty PostalWishCountProperty = DependencyProperty.Register(nameof(PostalWishCount), typeof(int), typeof(MailWindow));
|
||||
public int PostalWishCount {
|
||||
get => (int)GetValue(PostalWishCountProperty);
|
||||
private set => SetValue(PostalWishCountProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PostalNoEmailCountProperty = DependencyProperty.Register("PostalNoEmailCount", typeof(int), typeof(MailWindow));
|
||||
public static readonly DependencyProperty PostalNoEmailCountProperty = DependencyProperty.Register(nameof(PostalNoEmailCount), typeof(int), typeof(MailWindow));
|
||||
public int PostalNoEmailCount {
|
||||
get => (int)GetValue(PostalNoEmailCountProperty);
|
||||
private set => SetValue(PostalNoEmailCountProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EmailAllCountProperty = DependencyProperty.Register("EmailAllCount", typeof(int), typeof(MailWindow));
|
||||
public static readonly DependencyProperty EmailAllCountProperty = DependencyProperty.Register(nameof(EmailAllCount), typeof(int), typeof(MailWindow));
|
||||
public int EmailAllCount {
|
||||
get => (int)GetValue(EmailAllCountProperty);
|
||||
private set => SetValue(EmailAllCountProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty EmailWishCountProperty = DependencyProperty.Register("EmailWishCount", typeof(int), typeof(MailWindow));
|
||||
public static readonly DependencyProperty EmailWishCountProperty = DependencyProperty.Register(nameof(EmailWishCount), typeof(int), typeof(MailWindow));
|
||||
public int EmailWishCount {
|
||||
get => (int)GetValue(EmailWishCountProperty);
|
||||
private set => SetValue(EmailWishCountProperty, value);
|
||||
|
@ -1,5 +1,6 @@
|
||||
<local:AdministrationWindow
|
||||
x:Class="Elwig.Windows.MemberAdminWindow"
|
||||
AutomationProperties.AutomationId="MemberAdminWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Elwig.Windows"
|
||||
@ -523,7 +524,7 @@
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
|
||||
HorizontalAlignment="Left" Margin="0,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
|
||||
<CheckBox x:Name="ContactEmailInput" Content="E-Mail" IsEnabled="False"
|
||||
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"
|
||||
Checked="ContactEmailInput_Changed" Unchecked="ContactEmailInput_Changed"
|
||||
HorizontalAlignment="Left" Margin="60,0,0,15" VerticalAlignment="Bottom" Grid.Column="1" Grid.ColumnSpan="2"/>
|
||||
|
||||
<Button x:Name="DeliveryButton" Content="Lieferungen" Click="DeliveryButton_Click" IsEnabled="False"
|
||||
|
@ -1243,7 +1243,11 @@ namespace Elwig.Windows {
|
||||
}
|
||||
|
||||
private new void EmailAddressInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
||||
base.EmailAddressInput_TextChanged(sender, evt);
|
||||
if (sender == EmailAddress1Input && ContactEmailInput.IsChecked == true) {
|
||||
InputTextChanged((TextBox)sender, Validator.CheckEmailAddress((TextBox)sender, true));
|
||||
} else {
|
||||
base.EmailAddressInput_TextChanged(sender, evt);
|
||||
}
|
||||
UpdateContactInfoVisibility(IsEditing || IsCreating);
|
||||
}
|
||||
|
||||
@ -1252,6 +1256,10 @@ namespace Elwig.Windows {
|
||||
UpdateContactInfoVisibility(IsEditing || IsCreating);
|
||||
}
|
||||
|
||||
private void ContactEmailInput_Changed(object sender, RoutedEventArgs evt) {
|
||||
EmailAddressInput_TextChanged(EmailAddress1Input, new TextChangedEventArgs(evt.RoutedEvent, UndoAction.None));
|
||||
}
|
||||
|
||||
private void KgDetailsButton_Click(object sender, RoutedEventArgs evt) {
|
||||
if (DefaultKgInput.SelectedItem is AT_Kg kg) {
|
||||
App.FocusOriginHierarchyKg(kg.KgNr);
|
||||
|
@ -13,7 +13,7 @@
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Installer\Installer.wixproj" />
|
||||
<PackageReference Include="WixToolset.Bal.wixext" Version="5.0.0" />
|
||||
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.0" />
|
||||
<PackageReference Include="WixToolset.Bal.wixext" Version="5.0.1" />
|
||||
<PackageReference Include="WixToolset.Util.wixext" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -22,11 +22,11 @@ namespace Tests.DocumentTests {
|
||||
Winzerstraße 1
|
||||
2223 Hohenruppersdorf
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Traubengutschrift Max Mustermann – Probevariante"));
|
||||
Assert.That(text, Contains.Substring("AT12 3456 7890 1234 5678"));
|
||||
Assert.That(text, Contains.Substring("AT81 1234 5678 9012 3457"));
|
||||
Assert.That(text, Contains.Substring("""
|
||||
20201001X001 1 Grüner Veltliner 73 15,0 ungeb.: 3 219 0,5000 - - 1 609,50
|
||||
20201001X003 1 Grüner Veltliner 75 15,4 ungeb.: 2 561 - -
|
||||
|
@ -19,9 +19,9 @@ namespace Tests.DocumentTests {
|
||||
Winzerstraße 1
|
||||
2223 Hohenruppersdorf
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Anlieferungsbestätigung 2020"));
|
||||
Assert.That(text, Contains.Substring("""
|
||||
20201001X003 2 Grüner Veltliner Kabinett Kabinett 87 17,6 ungeb.: 3 129 3 129 n
|
||||
|
@ -17,9 +17,9 @@ namespace Tests.DocumentTests {
|
||||
Winzerstraße 1
|
||||
2223 Hohenruppersdorf
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X001"));
|
||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||
Assert.That(text, Contains.Substring("""
|
||||
@ -44,9 +44,9 @@ namespace Tests.DocumentTests {
|
||||
Winzerstraße 2
|
||||
2223 Hohenruppersdorf
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("4725836")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("0123471")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X004"));
|
||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||
Assert.That(text, Contains.Substring("""
|
||||
@ -77,9 +77,9 @@ namespace Tests.DocumentTests {
|
||||
Winzerstraße 1
|
||||
2223 Hohenruppersdorf
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("1472583")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("0123463")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201001X003"));
|
||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||
Assert.That(text, Contains.Substring("""
|
||||
@ -116,9 +116,9 @@ namespace Tests.DocumentTests {
|
||||
Brünner Straße 10
|
||||
2120 Wolkersdorf im Weinviertel
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("7258369")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("0123480")); // Betriebsnummer
|
||||
Assert.That(text, Contains.Substring("pauschaliert"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {DateTime.Now:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring($"Wolkersdorf, am {Elwig.Helpers.Utils.Today:dd.MM.yyyy}"));
|
||||
Assert.That(text, Contains.Substring("Traubenübernahmeschein Nr. 20201002X001"));
|
||||
Assert.That(text, Contains.Substring("Ich bin der Text, der auf einem Traubenübernahmeschein steht!"));
|
||||
Assert.That(text, Contains.Substring("""
|
||||
|
@ -32,8 +32,8 @@ namespace Tests.DocumentTests {
|
||||
Adresse: Hauptstraße 1
|
||||
PLZ/Ort: 2122 Riedenthal (Riedenthal)
|
||||
"""));
|
||||
Assert.That(text, Contains.Substring("IBAN: AT12 3456 7890 1234 5678"));
|
||||
Assert.That(text, Contains.Substring("Betriebs-Nr.: 2583691"));
|
||||
Assert.That(text, Contains.Substring("IBAN: AT97 1234 5678 9012 3460"));
|
||||
Assert.That(text, Contains.Substring("Betriebs-Nr.: 0123498"));
|
||||
Assert.That(text, Contains.Substring("Stammgemeinde: Wolkersdorf"));
|
||||
});
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ namespace Tests.DocumentTests {
|
||||
Assert.That(text, Contains.Substring("Mitgliederliste"));
|
||||
Assert.That(text, Contains.Substring("Alle Mitglieder"));
|
||||
Assert.That(table.Take(3), Is.EqualTo(new string[][] {
|
||||
["101 MUSTERMANN Max", "Winzerstraße 1", "2223", "Hohenruppersdorf", "1472583", "0", "Hohenruppersdorf"],
|
||||
["102 WEINBAUER Wernhardt", "Winzerstraße 2", "2223", "Hohenruppersdorf", "4725836", "0", "Hohenruppersdorf"],
|
||||
["", "W&B Weinbauer GesbR", "Winzerstraße 2", "2223", "Hohenruppersdorf"],
|
||||
["101 MUSTERMANN Max", "Winzerstraße 1", "2223", "Hohenruppersdorf", "0123463", "0", "Hohenruppersdorf"],
|
||||
["102 WEINBAUER Wernhardt", "Winzerstraße 2", "2223", "Hohenruppersdorf", "0123471", "0", "Hohenruppersdorf"],
|
||||
[ "W&B Weinbauer GesbR", "Winzerstraße 2", "2223", "Hohenruppersdorf"],
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace Tests.DocumentTests {
|
||||
|
||||
public static string[][] ExtractTable(string text) {
|
||||
return text.Split('\n')
|
||||
.Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).ToArray())
|
||||
.Select(row => Regex.Split(row, @"\s{2,}").Select(c => c.Trim()).Where(c => c.Length > 0).ToArray())
|
||||
.Where(row => row.Length > 3)
|
||||
.Skip(1)
|
||||
.ToArray();
|
||||
|
49
Tests/E2ETests/AppSession.cs
Normal file
49
Tests/E2ETests/AppSession.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using OpenQA.Selenium.Appium;
|
||||
|
||||
namespace Tests.E2ETests {
|
||||
public class AppSession : IDisposable {
|
||||
|
||||
private const int WaitForAppLaunch = 3;
|
||||
private readonly string WinAppDriverUrl;
|
||||
public readonly WindowsDriver<WindowsElement> App;
|
||||
public readonly WindowsDriver<WindowsElement> Desktop;
|
||||
|
||||
public AppSession(string appPath, string? appArgs, string winAppDriverUrl) {
|
||||
WinAppDriverUrl = winAppDriverUrl;
|
||||
var appiumOptions = new AppiumOptions();
|
||||
appiumOptions.AddAdditionalCapability("app", appPath);
|
||||
if (appArgs != null)
|
||||
appiumOptions.AddAdditionalCapability("appArguments", appArgs);
|
||||
appiumOptions.AddAdditionalCapability("deviceName", "WindowsPC");
|
||||
appiumOptions.AddAdditionalCapability("ms:waitForAppLaunch", WaitForAppLaunch);
|
||||
App = new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), appiumOptions);
|
||||
Assert.That(App, Is.Not.Null);
|
||||
Assert.That(App.SessionId, Is.Not.Null);
|
||||
App.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(1.5);
|
||||
var desktopOptions = new AppiumOptions();
|
||||
desktopOptions.AddAdditionalCapability("app", "Root");
|
||||
desktopOptions.AddAdditionalCapability("deviceName", "WindowsPC");
|
||||
Desktop = new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), desktopOptions);
|
||||
}
|
||||
|
||||
public WindowsDriver<WindowsElement> CreateWindowDriver(string windowName) {
|
||||
var window = Desktop.FindElementByAccessibilityId(windowName);
|
||||
var windowHandle = int.Parse(window.GetAttribute("NativeWindowHandle")).ToString("x"); // Convert to Hex
|
||||
var appiumOptions = new AppiumOptions();
|
||||
appiumOptions.AddAdditionalCapability("appTopLevelWindow", windowHandle);
|
||||
return new WindowsDriver<WindowsElement>(new Uri(WinAppDriverUrl), appiumOptions);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
App.Close();
|
||||
try {
|
||||
Desktop.FindElement(By.Name("Ja")).Click();
|
||||
} catch { }
|
||||
App.Dispose();
|
||||
Desktop.Close();
|
||||
Desktop.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
85
Tests/E2ETests/DeliveryAdminWindowReceiptTest.cs
Normal file
85
Tests/E2ETests/DeliveryAdminWindowReceiptTest.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
using Tests.WeighingTests;
|
||||
|
||||
namespace Tests.E2ETests {
|
||||
[TestFixture, Order(2)]
|
||||
public class DeliveryAdminWindowReceiptTest {
|
||||
|
||||
private MockScale Mock;
|
||||
private AppSession Session;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup() {
|
||||
Mock = new CommandMockScale(12345, ScaleHandlers.Handle_IT3000A) {
|
||||
Weight = 3210,
|
||||
};
|
||||
Session = new(Utils.ApplicationPath, Utils.ConfigPath, WinAppDriver.WinAppDriverUrl);
|
||||
Session.App.FindElement(By.Name("Stammdaten")).Click();
|
||||
Thread.Sleep(500);
|
||||
var window = Session.CreateWindowDriver("BaseDataWindow");
|
||||
window.FindElement(By.Name("Saisons")).Click();
|
||||
window.FindElement(By.Name("Neu anlegen...")).Click();
|
||||
var dialog = Session.CreateWindowDriver("NewSeasonDialog");
|
||||
dialog.FindElement(By.Name("Bestätigen")).Click();
|
||||
dialog.Close();
|
||||
Thread.Sleep(500);
|
||||
window.Close();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void Teardown() {
|
||||
Session.Dispose();
|
||||
Mock.Dispose();
|
||||
}
|
||||
|
||||
private WindowsDriver<WindowsElement> OpenReceiptWindow() {
|
||||
Session.App.FindElement(By.Name("Übernahme")).Click();
|
||||
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||
return Session.CreateWindowDriver("DeliveryAdminWindow");
|
||||
}
|
||||
|
||||
private void FinishDeliveryNote(WindowsDriver<WindowsElement> window) {
|
||||
window.FindElement(By.Name("Abschließen")).Click();
|
||||
Thread.Sleep(2000);
|
||||
var doc = Session.CreateWindowDriver("DocumentViewerWindow");
|
||||
Assert.That(doc.Title, Contains.Substring("Traubenübernahmeschein"));
|
||||
Thread.Sleep(500);
|
||||
doc.Close();
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_1_Minimal() {
|
||||
var window = OpenReceiptWindow();
|
||||
window.FindElement(By.WpfId("MgNrInput")).SendKeys("101" + Keys.Enter + "GV" + Keys.Enter + "73" + Keys.Enter + Keys.Enter);
|
||||
Thread.Sleep(500);
|
||||
FinishDeliveryNote(window);
|
||||
window.Close();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_2_OtherInputs() {
|
||||
var window = OpenReceiptWindow();
|
||||
window.FindElement(By.WpfId("MemberInput")).SendKeys("Mustermann Max");
|
||||
window.FindElement(By.WpfId("WineVarietyInput")).SelectItem("Zweigelt");
|
||||
window.FindElement(By.WpfId("GradationKmwInput")).SendKeys("18");
|
||||
window.FindElement(By.Name("Wiegen")).Click();
|
||||
Thread.Sleep(500);
|
||||
FinishDeliveryNote(window);
|
||||
window.Close();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_3_AttributeCultivationModifier() {
|
||||
var window = OpenReceiptWindow();
|
||||
window.FindElement(By.WpfId("MgNrInput")).SendKeys("102" + Keys.Enter + "GVK");
|
||||
window.FindElement(By.WpfId("CultivationInput")).SelectItem("Bio");
|
||||
window.FindElement(By.WpfId("GradationOeInput")).SendKeys("73" + Keys.Enter + Keys.Enter);
|
||||
|
||||
Thread.Sleep(500);
|
||||
FinishDeliveryNote(window);
|
||||
window.Close();
|
||||
}
|
||||
}
|
||||
}
|
50
Tests/E2ETests/MainWindowTest.cs
Normal file
50
Tests/E2ETests/MainWindowTest.cs
Normal file
@ -0,0 +1,50 @@
|
||||
namespace Tests.E2ETests {
|
||||
[TestFixture, Order(1)]
|
||||
public class MainWindowTest {
|
||||
|
||||
private AppSession Session;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup() {
|
||||
Session = new(Utils.ApplicationPath, Utils.ConfigPath, WinAppDriver.WinAppDriverUrl);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void Teardown() {
|
||||
Session.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_Open_MemberAdminWindow() {
|
||||
Assert.DoesNotThrow(() => {
|
||||
Session.App.FindElement(By.Name("Mitglieder")).Click();
|
||||
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||
var window = Session.CreateWindowDriver("MemberAdminWindow");
|
||||
Assert.That(window.Title, Is.EqualTo("Mitglieder - Elwig"));
|
||||
window.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_Open_DeliveryAdminWindow() {
|
||||
Assert.DoesNotThrow(() => {
|
||||
Session.App.FindElement(By.Name("Lieferungen")).Click();
|
||||
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||
var window = Session.CreateWindowDriver("DeliveryAdminWindow");
|
||||
Assert.That(window.Title, Is.EqualTo("Lieferungen - Elwig"));
|
||||
window.Close();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_Open_BaseDataWindow() {
|
||||
Assert.DoesNotThrow(() => {
|
||||
Session.App.FindElement(By.Name("Stammdaten")).Click();
|
||||
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||
var window = Session.CreateWindowDriver("BaseDataWindow");
|
||||
Assert.That(window.Title, Is.EqualTo("Stammdaten - Elwig"));
|
||||
window.Close();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
146
Tests/E2ETests/MemberAdminWindowTest.cs
Normal file
146
Tests/E2ETests/MemberAdminWindowTest.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using OpenQA.Selenium.Appium.Windows;
|
||||
|
||||
namespace Tests.E2ETests {
|
||||
[TestFixture, Order(3)]
|
||||
public class MemberAdminWindowTest {
|
||||
|
||||
private AppSession Session;
|
||||
private WindowsDriver<WindowsElement> Window;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void WindowSetup() {
|
||||
Session = new(Utils.ApplicationPath, Utils.ConfigPath, WinAppDriver.WinAppDriverUrl);
|
||||
Session.App.FindElement(By.Name("Mitglieder")).Click();
|
||||
Thread.Sleep(Utils.WINDOW_OPEN_SLEEP);
|
||||
Window = Session.CreateWindowDriver("MemberAdminWindow");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void WindowTeardown() {
|
||||
Window.Close();
|
||||
Window.Quit();
|
||||
Session.Dispose();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Teardown() {
|
||||
Window.FindElement(By.WpfId("SearchInput")).Clear();
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_1_CreateMember() {
|
||||
Window.FindElement(By.WpfId("NewMemberButton")).Click();
|
||||
|
||||
Window.FindElement(By.WpfId("MgNrInput")).Clear();
|
||||
Window.FindElement(By.WpfId("MgNrInput")).SendKeys("9999");
|
||||
|
||||
Window.FindElement(By.WpfId("GivenNameInput")).SendKeys("Norbert");
|
||||
Window.FindElement(By.WpfId("FamilyNameInput")).SendKeys("Neuling");
|
||||
Window.FindElement(By.WpfId("PrefixInput")).SendKeys("Ing.");
|
||||
Window.FindElement(By.WpfId("SuffixInput")).SendKeys("jun.");
|
||||
Window.FindElement(By.WpfId("BirthdayInput")).SendKeys("1987");
|
||||
|
||||
Window.FindElement(By.WpfId("AddressInput")).SendKeys("Musterstraße 9");
|
||||
Window.FindElement(By.WpfId("PlzInput")).SendKeys("2120");
|
||||
Window.FindElement(By.WpfId("OrtInput")).SelectItem(1);
|
||||
|
||||
Window.FindElement(By.WpfId("EmailAddress1Input")).SendKeys("norbert.neuling@aon.at");
|
||||
Window.FindElement(By.WpfId("EmailAddress2Input")).SendKeys("nathalie.neuling@aon.at");
|
||||
|
||||
Window.FindElement(By.WpfId("PhoneNr1TypeInput")).SelectItem(1);
|
||||
Window.FindElement(By.WpfId("PhoneNr1Input")).SendKeys("012345678");
|
||||
|
||||
Window.FindElement(By.WpfId("PhoneNr2TypeInput")).SelectItem(2);
|
||||
Window.FindElement(By.WpfId("PhoneNr2Input")).SendKeys("0664123456");
|
||||
|
||||
Window.FindElement(By.WpfId("IbanInput")).SendKeys("AT611904300234573201");
|
||||
Window.FindElement(By.WpfId("BicInput")).SendKeys("RLNWATWWWDF");
|
||||
|
||||
Window.FindElement(By.WpfId("UstIdNrInput")).SendKeys("ATU66192906"); // TODO: Testdaten?
|
||||
Window.FindElement(By.WpfId("LfbisNrInput")).SendKeys("1251074"); // TODO: Testdaten?
|
||||
|
||||
Window.FindElement(By.WpfId("BuchführendInput")).Click();
|
||||
Window.FindElement(By.WpfId("OrganicInput")).Click();
|
||||
|
||||
Window.FindElement(By.WpfId("BillingNameInput")).SendKeys("Neuling KG");
|
||||
Window.FindElement(By.WpfId("BillingAddressInput")).SendKeys("Betriebsstraße 1");
|
||||
Window.FindElement(By.WpfId("BillingPlzInput")).SendKeys("2120");
|
||||
Window.FindElement(By.WpfId("BillingOrtInput")).SelectItem(2);
|
||||
|
||||
Window.FindElement(By.WpfId("BusinessSharesInput")).SendKeys("10");
|
||||
Window.FindElement(By.WpfId("BranchInput")).SelectItem("Matzen");
|
||||
Window.FindElement(By.WpfId("DefaultKgInput")).SelectItem(3);
|
||||
|
||||
Window.FindElement(By.WpfId("VollLieferantInput")).Click();
|
||||
Window.FindElement(By.WpfId("FunkionärInput")).Click();
|
||||
|
||||
Window.FindElement(By.WpfId("CommentInput")).SendKeys("Die lieben Mustermänner und Musterfrauen!");
|
||||
Window.FindElement(By.WpfId("ContactEmailInput")).Click();
|
||||
|
||||
Window.FindElement(By.WpfId("SaveButton")).Click();
|
||||
|
||||
Window.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
|
||||
Thread.Sleep(500);
|
||||
var memberListRow = Window.FindElement(By.WpfId("MemberList")).FindElement(By.ClassName("DataGridRow"));
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(memberListRow, Is.Not.Null);
|
||||
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
|
||||
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
|
||||
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_2_EditMember() {
|
||||
Window!.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
|
||||
Thread.Sleep(500);
|
||||
var memberList = Window.FindElement(By.WpfId("MemberList"));
|
||||
Assert.That(memberList, Is.Not.Null);
|
||||
|
||||
var memberListRows = memberList.FindElements(By.ClassName("DataGridRow"));
|
||||
Assert.That(memberListRows, Has.Count.EqualTo(1));
|
||||
|
||||
Window.FindElement(By.WpfId("EditMemberButton")).Click();
|
||||
|
||||
var businessSharesInput = Window.FindElement(By.WpfId("BusinessSharesInput"));
|
||||
Assert.That(businessSharesInput, Is.Not.Null);
|
||||
|
||||
var businessShares = int.Parse(businessSharesInput.Text);
|
||||
businessSharesInput.Clear();
|
||||
businessSharesInput.SendKeys($"{businessShares + 5}");
|
||||
|
||||
Window.FindElement(By.WpfId("SaveButton")).Click();
|
||||
|
||||
var newBusinessShares = int.Parse(businessSharesInput.Text);
|
||||
Assert.That(newBusinessShares, Is.EqualTo(businessShares + 5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_3_DeleteMember() {
|
||||
Window!.FindElement(By.WpfId("SearchInput")).SendKeys("9999");
|
||||
Thread.Sleep(500);
|
||||
var memberList = Window.FindElement(By.WpfId("MemberList"));
|
||||
Assert.That(memberList, Is.Not.Null);
|
||||
|
||||
var memberListRows = memberList.FindElements(By.ClassName("DataGridRow"));
|
||||
Assert.That(memberListRows, Has.Count.EqualTo(1));
|
||||
|
||||
var memberListRow = memberListRows.First();
|
||||
Assert.Multiple(() => {
|
||||
Assert.That(memberListRow, Is.Not.Null);
|
||||
Assert.That(memberListRow.FindElement(By.Name("9999 ")), Is.Not.Null);
|
||||
Assert.That(memberListRow.FindElement(By.Name("Norbert")), Is.Not.Null);
|
||||
Assert.That(memberListRow.FindElement(By.Name("Neuling")), Is.Not.Null);
|
||||
});
|
||||
|
||||
Window.FindElement(By.WpfId("DeleteMemberButton")).Click();
|
||||
var dialog = Session.CreateWindowDriver("DeleteMemberDialog");
|
||||
dialog.FindElement(By.WpfId("NameInput")).SendKeys("9999 Ing. Norbert Neuling jun.");
|
||||
dialog.FindElement(By.WpfId("ConfirmButton")).Click();
|
||||
|
||||
memberListRows = memberList.FindElements(By.ClassName("DataGridRow"));
|
||||
Assert.That(memberListRows, Has.Count.EqualTo(0));
|
||||
}
|
||||
}
|
||||
}
|
37
Tests/E2ETests/Setup.cs
Normal file
37
Tests/E2ETests/Setup.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using Elwig.Helpers;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Tests.E2ETests {
|
||||
[SetUpFixture]
|
||||
public static class Setup {
|
||||
|
||||
private static WinAppDriver? Driver;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public static void SetupWinAppDriver() {
|
||||
Driver = new();
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public static async Task SetupDatabase() {
|
||||
if (File.Exists(Utils.TestDatabasePath)) File.Delete(Utils.TestDatabasePath);
|
||||
using var cnx = await AppDbContext.ConnectAsync($"Data Source=\"{Utils.TestDatabasePath}\"; Mode=ReadWriteCreate; Foreign Keys=True; Cache=Default");
|
||||
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Create.sql");
|
||||
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.Insert.sql");
|
||||
await AppDbContext.ExecuteEmbeddedScript(cnx, Assembly.GetExecutingAssembly(), "Tests.Resources.Sql.E2EInsert.sql");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public static void TeardownWinAppDriver() {
|
||||
Driver?.Dispose();
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public static void TeardownDatabase() {
|
||||
try {
|
||||
// FIXME not working - other process using file
|
||||
File.Delete(Utils.TestDatabasePath);
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
}
|
31
Tests/E2ETests/Utils.cs
Normal file
31
Tests/E2ETests/Utils.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using OpenQA.Selenium;
|
||||
using OpenQA.Selenium.Appium;
|
||||
|
||||
namespace Tests.E2ETests {
|
||||
public static class Utils {
|
||||
|
||||
public const int WINDOW_OPEN_SLEEP = 2000;
|
||||
|
||||
public static readonly string ApplicationPath = Path.GetFullPath(@"..\..\..\..\Elwig\bin\Debug\net8.0-windows\Elwig.exe");
|
||||
public static readonly string ConfigPath = Path.GetFullPath(@"..\..\..\..\Tests\config.test.ini");
|
||||
public static readonly string TestDatabasePath = Path.GetFullPath(@"..\..\..\..\Tests\ElwigTestDB.sqlite3");
|
||||
|
||||
public static void SelectItem(this IWebElement element, int count) {
|
||||
element.Click();
|
||||
element.SendKeys(string.Concat(Enumerable.Repeat(Keys.Down, count)));
|
||||
element.SendKeys(Keys.Enter);
|
||||
}
|
||||
|
||||
public static void SelectItem(this IWebElement element, string text) {
|
||||
element.Click();
|
||||
element.SendKeys(text);
|
||||
element.SendKeys(Keys.Enter);
|
||||
}
|
||||
}
|
||||
|
||||
public class By : OpenQA.Selenium.By {
|
||||
public static OpenQA.Selenium.By WpfId(string wpfName) {
|
||||
return new ByAccessibilityId(wpfName);
|
||||
}
|
||||
}
|
||||
}
|
27
Tests/E2ETests/WinAppDriver.cs
Normal file
27
Tests/E2ETests/WinAppDriver.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Tests.E2ETests {
|
||||
public class WinAppDriver : IDisposable {
|
||||
|
||||
public const string WinAppDriverUrl = "http://127.0.0.1:4723";
|
||||
private const string WinAppDriverPath = @"C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe";
|
||||
private readonly Process WinAppDriverProcess;
|
||||
|
||||
public WinAppDriver() {
|
||||
WinAppDriverProcess = Process.Start(new ProcessStartInfo(WinAppDriverPath) {
|
||||
//UseShellExecute = true,
|
||||
//Verb = "runas", // run as administrator
|
||||
RedirectStandardInput = true,
|
||||
EnvironmentVariables = {
|
||||
// { "DX.UITESTINGENABLED", "1" },
|
||||
}
|
||||
})!;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
GC.SuppressFinalize(this);
|
||||
WinAppDriverProcess.StandardInput.WriteLine("");
|
||||
WinAppDriverProcess.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -31,9 +31,9 @@ INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg
|
||||
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL),
|
||||
(2021, 'EUR', 2000, 4000, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_select) VALUES
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
|
||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE);
|
||||
|
||||
-- Test 01
|
||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||
|
@ -9,9 +9,9 @@ INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lo
|
||||
INSERT INTO season (year, currency, min_kg_per_bs, max_kg_per_bs, penalty_per_kg, penalty_amount, penalty_none, start_date, end_date) VALUES
|
||||
(2020, 'EUR', 1000, 2000, NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, standard, quick_select) VALUES
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, FALSE, FALSE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, FALSE, FALSE);
|
||||
INSERT INTO modifier (year, modid, ordering, name, abs, rel, active) VALUES
|
||||
(2020, 'S', 0, 'Geschädigte Trauben', NULL, -0.1, TRUE),
|
||||
(2020, 'A', 0, 'Keine Voranmeldung', -1000, NULL, TRUE);
|
||||
|
||||
INSERT INTO delivery (mgnr, year, did, date, time, zwstid, lnr) VALUES
|
||||
(101, 2020, 1, '2020-10-01', '09:03:12', 'X', 1),
|
||||
|
9
Tests/Resources/Sql/E2EInsert.sql
Normal file
9
Tests/Resources/Sql/E2EInsert.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- inserts for E2ETests
|
||||
|
||||
INSERT INTO wine_cultivation (cultid, name, description) VALUES
|
||||
('KIP', 'KIP', 'Kontrollierte Integrierte Produktion'),
|
||||
('B', 'Bio', 'AT-BIO-302');
|
||||
|
||||
INSERT INTO wine_attribute (attrid, name, active, max_kg_per_ha, strict, fill_lower) VALUES
|
||||
('K', 'Kabinett', TRUE, NULL, FALSE, 0),
|
||||
('D', 'DAC', TRUE, 7500, FALSE, 0);
|
@ -42,7 +42,9 @@ INSERT INTO client_parameter (param, value) VALUES
|
||||
('CLIENT_PLZ', '2120'),
|
||||
('CLIENT_ORT', 'Wolkersdorf'),
|
||||
('CLIENT_ADDRESS', 'Genossenschaftsstraße 1'),
|
||||
('CLIENT_IBAN', 'AT12 3456 7890 1234 5678'),
|
||||
('CLIENT_IBAN', 'AT11 1234 5678 9012 3456'),
|
||||
('CLIENT_LFBISNR', '0123455'),
|
||||
('CLIENT_USTIDNR', 'ATU12345675'),
|
||||
('TEXT_DELIVERYNOTE', 'Ich bin der Text, der auf einem Traubenübernahmeschein steht!');
|
||||
|
||||
INSERT INTO branch (zwstid, name, country, postal_dest, address) VALUES
|
||||
@ -64,11 +66,11 @@ INSERT INTO wb_kg (kgnr, glnr) VALUES
|
||||
(15216, 2),
|
||||
(15224, 2);
|
||||
|
||||
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr) VALUES
|
||||
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT123456789012345678', '1472583'),
|
||||
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT123456789012345678', '4725836'),
|
||||
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT123456789012345678', '7258369'),
|
||||
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 40, 212005138, 'Wiener Straße 15', 15224, 'AT123456789012345678', '2583691');
|
||||
INSERT INTO member (mgnr, given_name, family_name, zwstid, volllieferant, buchführend, country, postal_dest, address, default_kgnr, iban, lfbis_nr, ustid_nr) VALUES
|
||||
(101, 'Max', 'Mustermann', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 1', 06109, 'AT811234567890123457', '0123463', NULL ),
|
||||
(102, 'Wernhardt', 'Weinbauer', 'X', FALSE, FALSE, 40, 222303524, 'Winzerstraße 2', 06109, 'AT541234567890123458', '0123471', 'ATU12345684'),
|
||||
(103, 'Matthäus', 'Musterbauer', 'X', FALSE, FALSE, 40, 212005138, 'Brünner Straße 10', 15224, 'AT271234567890123459', '0123480', NULL ),
|
||||
(104, 'Waltraud', 'Winzer', 'X', FALSE, FALSE, 40, 212005138, 'Wiener Straße 15', 15224, 'AT971234567890123460', '0123498', 'ATU12345693');
|
||||
|
||||
INSERT INTO member_billing_address (mgnr, name, country, postal_dest, address) VALUES
|
||||
(102, 'W&B Weinbauer GesbR', 40, 222303524, 'Winzerstraße 2'),
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
|
||||
<PackageReference Include="NReco.PdfRenderer" Version="1.5.3" />
|
||||
<PackageReference Include="NUnit" Version="4.1.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace Tests.WeighingTests {
|
||||
public abstract class MockScale : IDisposable {
|
||||
|
104
Tests/WeighingTests/ScaleHandlers.cs
Normal file
104
Tests/WeighingTests/ScaleHandlers.cs
Normal file
@ -0,0 +1,104 @@
|
||||
namespace Tests.WeighingTests {
|
||||
public static class ScaleHandlers {
|
||||
|
||||
public static (string, bool) Handle_IT3000A(string req, int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var overloaded = modes.Contains("overloaded");
|
||||
var moving = modes.Contains("moving");
|
||||
var invalid = modes.Contains("invalid");
|
||||
var crc = modes.Contains("crc");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
Thread.Sleep(100);
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||
return ("<31>\r\n", false);
|
||||
}
|
||||
req = req[1..^1];
|
||||
|
||||
bool incr;
|
||||
if (req.Length > 3) {
|
||||
return ("<32>\r\n", false);
|
||||
} else if (req.StartsWith("RN")) {
|
||||
incr = true;
|
||||
} else if (req.StartsWith("RM")) {
|
||||
incr = false;
|
||||
} else {
|
||||
return ("<32>\r\n", false);
|
||||
}
|
||||
|
||||
if (overloaded) {
|
||||
return ("<12>\r\n", false);
|
||||
} else if (weight == 0) {
|
||||
incr = false;
|
||||
}
|
||||
|
||||
if (moving && incr)
|
||||
return ("<13>\r\n", false);
|
||||
|
||||
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 15, 12, 34, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"1",3}";
|
||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||
if (crc) checksum += 10;
|
||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||
}
|
||||
|
||||
public static (string, bool) Handle_L246(string req, int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var overloaded = modes.Contains("overloaded");
|
||||
var moving = modes.Contains("moving");
|
||||
var invalid = modes.Contains("invalid");
|
||||
var crc = modes.Contains("crc");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
Thread.Sleep(100);
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||
return ("<31>\r\n", false);
|
||||
}
|
||||
req = req[1..^1];
|
||||
|
||||
bool incr;
|
||||
if (req.Length > 3) {
|
||||
return ("<32>\r\n", false);
|
||||
} else if (req.StartsWith("RN")) {
|
||||
incr = true;
|
||||
} else if (req.StartsWith("RM")) {
|
||||
incr = false;
|
||||
} else {
|
||||
return ("<32>\r\n", false);
|
||||
}
|
||||
|
||||
if (overloaded)
|
||||
return ("<12>\r\n", false);
|
||||
|
||||
if (moving && incr)
|
||||
return ("<13>\r\n", false);
|
||||
|
||||
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 17, 14, 23, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"001",3}";
|
||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||
if (crc) checksum += 10;
|
||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||
}
|
||||
|
||||
public static (string, bool) Handle_L320(int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var invalid = modes.Contains("invalid");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
Thread.Sleep(100);
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
}
|
||||
|
||||
bool incr = true;
|
||||
return ($" {new DateTime(2020, 9, 28, 9, 8, 0):dd.MM.yy HH:mm} {identNr,4} {weight,9}{(unit ? "lb" : "kg")} \r\n", incr);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,49 +4,36 @@ namespace Tests.WeighingTests {
|
||||
[TestFixture]
|
||||
public class ScaleTestBadenL320 {
|
||||
|
||||
private EventMockScale? Mock;
|
||||
private AveryEventScale? Scale;
|
||||
|
||||
private static (string, bool) ScaleHandler(int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var invalid = modes.Contains("invalid");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
}
|
||||
|
||||
bool incr = true;
|
||||
return ($" {new DateTime(2020, 9, 28, 9, 8, 0):dd.MM.yy HH:mm} {identNr,4} {weight,9}{(unit ? "lb" : "kg")} \r\n", incr);
|
||||
}
|
||||
private EventMockScale Mock;
|
||||
private AveryEventScale Scale;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetupScale() {
|
||||
Mock = new EventMockScale(12345, ScaleHandler);
|
||||
Mock = new EventMockScale(12345, ScaleHandlers.Handle_L320);
|
||||
Scale = new("1", "L320", "tcp://127.0.0.1:12345");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TeardownScale() {
|
||||
Mock?.Dispose();
|
||||
Scale?.Dispose();
|
||||
Mock.Dispose();
|
||||
Scale.Dispose();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ResetScale() {
|
||||
Mock!.IdentNr = 0;
|
||||
Mock!.Weight = 0;
|
||||
Mock!.Error = null;
|
||||
Mock.IdentNr = 0;
|
||||
Mock.Weight = 0;
|
||||
Mock.Error = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_01_Normal() {
|
||||
WeighingResult? res = null;
|
||||
Scale!.WeighingEvent += (sender, evt) => {
|
||||
Scale.WeighingEvent += (sender, evt) => {
|
||||
res = evt.Result;
|
||||
};
|
||||
|
||||
await Mock!.Weigh(2345);
|
||||
await Mock.Weigh(2345);
|
||||
await Task.Delay(100);
|
||||
Assert.That(res, Is.Not.Null);
|
||||
Assert.That(res, Is.EqualTo(new WeighingResult {
|
||||
@ -55,7 +42,7 @@ namespace Tests.WeighingTests {
|
||||
Date = new DateOnly(2020, 9, 28), Time = new TimeOnly(9, 8),
|
||||
}));
|
||||
|
||||
await Mock!.Weigh(4215);
|
||||
await Mock.Weigh(4215);
|
||||
await Task.Delay(100);
|
||||
Assert.That(res, Is.Not.Null);
|
||||
Assert.That(res, Is.EqualTo(new WeighingResult {
|
||||
|
@ -4,78 +4,39 @@ namespace Tests.WeighingTests {
|
||||
[TestFixture]
|
||||
class ScaleTestGrInzersdorfL246 {
|
||||
|
||||
private MockScale? Mock;
|
||||
private SysTecITScale? Scale;
|
||||
|
||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var overloaded = modes.Contains("overloaded");
|
||||
var moving = modes.Contains("moving");
|
||||
var invalid = modes.Contains("invalid");
|
||||
var crc = modes.Contains("crc");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||
return ("<31>\r\n", false);
|
||||
}
|
||||
req = req[1..^1];
|
||||
|
||||
bool incr;
|
||||
if (req.Length > 3) {
|
||||
return ("<32>\r\n", false);
|
||||
} else if (req.StartsWith("RN")) {
|
||||
incr = true;
|
||||
} else if (req.StartsWith("RM")) {
|
||||
incr = false;
|
||||
} else {
|
||||
return ("<32>\r\n", false);
|
||||
}
|
||||
|
||||
if (overloaded)
|
||||
return ("<12>\r\n", false);
|
||||
|
||||
if (moving && incr)
|
||||
return ("<13>\r\n", false);
|
||||
|
||||
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 17, 14, 23, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"001",3}";
|
||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||
if (crc) checksum += 10;
|
||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||
}
|
||||
private MockScale Mock;
|
||||
private SysTecITScale Scale;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetupScale() {
|
||||
Mock = new CommandMockScale(12345, ScaleHandler);
|
||||
Mock = new CommandMockScale(12345, ScaleHandlers.Handle_L246);
|
||||
Scale = new("1", "L246", "tcp://127.0.0.1:12345");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TeardownScale() {
|
||||
Mock?.Dispose();
|
||||
Scale?.Dispose();
|
||||
Mock.Dispose();
|
||||
Scale.Dispose();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ResetScale() {
|
||||
Mock!.IdentNr = 0;
|
||||
Mock!.Weight = 0;
|
||||
Mock!.Error = null;
|
||||
Mock.IdentNr = 0;
|
||||
Mock.Weight = 0;
|
||||
Mock.Error = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_01_CurrentWeight() {
|
||||
Mock!.Weight = 1235;
|
||||
Mock.Weight = 1235;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1235, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||
}));
|
||||
Mock!.Weight = 1240;
|
||||
Mock.Weight = 1240;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1240, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||
}));
|
||||
Mock!.Weight = 1245;
|
||||
Mock.Weight = 1245;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1245, Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||
}));
|
||||
@ -83,19 +44,19 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[Test]
|
||||
public async Task Test_02_Normal() {
|
||||
Mock!.Weight = 1235;
|
||||
Mock.Weight = 1235;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1235, WeighingId = "1",
|
||||
FullWeighingId = $"2020-10-17/1",
|
||||
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||
}));
|
||||
Mock!.Weight = 3335;
|
||||
Mock.Weight = 3335;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3335, WeighingId = "2",
|
||||
FullWeighingId = $"2020-10-17/2",
|
||||
Date = new DateOnly(2020, 10, 17), Time = new TimeOnly(14, 23),
|
||||
}));
|
||||
Mock!.Weight = 6420;
|
||||
Mock.Weight = 6420;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 6420, WeighingId = "3",
|
||||
FullWeighingId = $"2020-10-17/3",
|
||||
@ -105,39 +66,39 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[Test]
|
||||
public void Test_03_Moving() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "moving";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "moving";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_04_Overloaded() {
|
||||
Mock!.Weight = 10_000;
|
||||
Mock!.Error = "overloaded";
|
||||
Mock.Weight = 10_000;
|
||||
Mock.Error = "overloaded";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_05_InvalidResponse() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "invalid";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "invalid";
|
||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_06_InvalidCrc() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "crc";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "crc";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_07_InvalidUnit() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "unit";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "unit";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
}
|
||||
}
|
||||
|
@ -4,81 +4,39 @@ namespace Tests.WeighingTests {
|
||||
[TestFixture]
|
||||
class ScaleTestMatzenIT3000A {
|
||||
|
||||
private MockScale? Mock;
|
||||
private SysTecITScale? Scale;
|
||||
|
||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
var overloaded = modes.Contains("overloaded");
|
||||
var moving = modes.Contains("moving");
|
||||
var invalid = modes.Contains("invalid");
|
||||
var crc = modes.Contains("crc");
|
||||
var unit = modes.Contains("unit");
|
||||
|
||||
if (invalid) {
|
||||
return ("abcd\r\n", false);
|
||||
} else if (!req.StartsWith('<') || !req.EndsWith('>')) {
|
||||
return ("<31>\r\n", false);
|
||||
}
|
||||
req = req[1..^1];
|
||||
|
||||
bool incr;
|
||||
if (req.Length > 3) {
|
||||
return ("<32>\r\n", false);
|
||||
} else if (req.StartsWith("RN")) {
|
||||
incr = true;
|
||||
} else if (req.StartsWith("RM")) {
|
||||
incr = false;
|
||||
} else {
|
||||
return ("<32>\r\n", false);
|
||||
}
|
||||
|
||||
if (overloaded) {
|
||||
return ("<12>\r\n", false);
|
||||
} else if (weight == 0) {
|
||||
incr = false;
|
||||
}
|
||||
|
||||
if (moving && incr)
|
||||
return ("<13>\r\n", false);
|
||||
|
||||
string data = $"00{(moving ? 1 : 0)}0{new DateTime(2020, 10, 15, 12, 34, 0):dd.MM.yyHH:mm}{(incr ? identNr : 0),4}1" +
|
||||
$"{weight,8}{0,8}{weight,8}{(unit ? "lb" : "kg")} {"1",3}";
|
||||
ushort checksum = Elwig.Helpers.Utils.CalcCrc16Modbus(data);
|
||||
if (crc) checksum += 10;
|
||||
return ($"<{data}{checksum,8}>\r\n", incr);
|
||||
}
|
||||
private MockScale Mock;
|
||||
private SysTecITScale Scale;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void SetupScale() {
|
||||
Mock = new CommandMockScale(12345, ScaleHandler);
|
||||
Mock = new CommandMockScale(12345, ScaleHandlers.Handle_IT3000A);
|
||||
Scale = new("1", "IT3000A", "tcp://127.0.0.1:12345");
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TeardownScale() {
|
||||
Mock?.Dispose();
|
||||
Scale?.Dispose();
|
||||
Mock.Dispose();
|
||||
Scale.Dispose();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ResetScale() {
|
||||
Mock!.IdentNr = 0;
|
||||
Mock!.Weight = 0;
|
||||
Mock!.Error = null;
|
||||
Mock.IdentNr = 0;
|
||||
Mock.Weight = 0;
|
||||
Mock.Error = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_01_CurrentWeight() {
|
||||
Mock!.Weight = 1234;
|
||||
Mock.Weight = 1234;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1234, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||
}));
|
||||
Mock!.Weight = 1235;
|
||||
Mock.Weight = 1235;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1235, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||
}));
|
||||
Mock!.Weight = 1236;
|
||||
Mock.Weight = 1236;
|
||||
Assert.That(await Scale!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1236, Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||
}));
|
||||
@ -86,19 +44,19 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[Test]
|
||||
public async Task Test_02_Normal() {
|
||||
Mock!.Weight = 1234;
|
||||
Mock.Weight = 1234;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1234, WeighingId = "1",
|
||||
FullWeighingId = $"2020-10-15/1",
|
||||
Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||
}));
|
||||
Mock!.Weight = 3333;
|
||||
Mock.Weight = 3333;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3333, WeighingId = "2",
|
||||
FullWeighingId = $"2020-10-15/2",
|
||||
Date = new DateOnly(2020, 10, 15), Time = new TimeOnly(12, 34),
|
||||
}));
|
||||
Mock!.Weight = 4321;
|
||||
Mock.Weight = 4321;
|
||||
Assert.That(await Scale!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 4321, WeighingId = "3",
|
||||
FullWeighingId = $"2020-10-15/3",
|
||||
@ -108,39 +66,39 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[Test]
|
||||
public void Test_03_Moving() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "moving";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "moving";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_04_Overloaded() {
|
||||
Mock!.Weight = 10_000;
|
||||
Mock!.Error = "overloaded";
|
||||
Mock.Weight = 10_000;
|
||||
Mock.Error = "overloaded";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_05_InvalidResponse() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "invalid";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "invalid";
|
||||
Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_06_InvalidCrc() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "crc";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "crc";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_07_InvalidUnit() {
|
||||
Mock!.Weight = 1_000;
|
||||
Mock!.Error = "unit";
|
||||
Mock.Weight = 1_000;
|
||||
Mock.Error = "unit";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await Scale!.Weigh());
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,10 @@ namespace Tests.WeighingTests {
|
||||
[TestFixture]
|
||||
class ScaleTestWolkersdorfIT6000E {
|
||||
|
||||
private MockScale? MockA;
|
||||
private MockScale? MockB;
|
||||
private SysTecITScale? ScaleA;
|
||||
private SysTecITScale? ScaleB;
|
||||
private MockScale MockA;
|
||||
private MockScale MockB;
|
||||
private SysTecITScale ScaleA;
|
||||
private SysTecITScale ScaleB;
|
||||
|
||||
private static (string, bool) ScaleHandler(string req, int weight, string? error, int identNr, string terminalNr) {
|
||||
var modes = error?.Split(';') ?? [];
|
||||
@ -58,37 +58,37 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TeardownScale() {
|
||||
MockA?.Dispose();
|
||||
MockB?.Dispose();
|
||||
ScaleA?.Dispose();
|
||||
ScaleB?.Dispose();
|
||||
MockA.Dispose();
|
||||
MockB.Dispose();
|
||||
ScaleA.Dispose();
|
||||
ScaleB.Dispose();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void ResetScale() {
|
||||
MockA!.IdentNr = 0;
|
||||
MockA!.Weight = 0;
|
||||
MockA!.Error = null;
|
||||
MockB!.IdentNr = 0;
|
||||
MockB!.Weight = 0;
|
||||
MockB!.Error = null;
|
||||
MockA.IdentNr = 0;
|
||||
MockA.Weight = 0;
|
||||
MockA.Error = null;
|
||||
MockB.IdentNr = 0;
|
||||
MockB.Weight = 0;
|
||||
MockB.Error = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_01_CurrentWeight() {
|
||||
MockA!.Weight = 1234;
|
||||
MockA.Weight = 1234;
|
||||
Assert.That(await ScaleA!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1234, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
MockB!.Weight = 3456;
|
||||
MockB.Weight = 3456;
|
||||
Assert.That(await ScaleB!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3456, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
MockA!.Weight = 1236;
|
||||
MockA.Weight = 1236;
|
||||
Assert.That(await ScaleA!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1236, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
MockB!.Weight = 3457;
|
||||
MockB.Weight = 3457;
|
||||
Assert.That(await ScaleB!.GetCurrentWeight(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3457, Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
@ -96,25 +96,25 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[Test]
|
||||
public async Task Test_02_Normal() {
|
||||
MockA!.Weight = 1234;
|
||||
MockA.Weight = 1234;
|
||||
Assert.That(await ScaleA!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 1234, WeighingId = "1",
|
||||
FullWeighingId = $"2020-10-08/1",
|
||||
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
MockB!.Weight = 3456;
|
||||
MockB.Weight = 3456;
|
||||
Assert.That(await ScaleB!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3456, WeighingId = "1",
|
||||
FullWeighingId = $"2020-10-08/1",
|
||||
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
MockA!.Weight = 4321;
|
||||
MockA.Weight = 4321;
|
||||
Assert.That(await ScaleA!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 4321, WeighingId = "2",
|
||||
FullWeighingId = $"2020-10-08/2",
|
||||
Date = new DateOnly(2020, 10, 8), Time = new TimeOnly(8, 47),
|
||||
}));
|
||||
MockB!.Weight = 3333;
|
||||
MockB.Weight = 3333;
|
||||
Assert.That(await ScaleB!.Weigh(), Is.EqualTo(new WeighingResult {
|
||||
Weight = 3333, WeighingId = "2",
|
||||
FullWeighingId = $"2020-10-08/2",
|
||||
@ -124,39 +124,39 @@ namespace Tests.WeighingTests {
|
||||
|
||||
[Test]
|
||||
public void Test_03_Moving() {
|
||||
MockA!.Weight = 1_000;
|
||||
MockA!.Error = "moving";
|
||||
MockA.Weight = 1_000;
|
||||
MockA.Error = "moving";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Bewegung"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_04_Overloaded() {
|
||||
MockA!.Weight = 10_000;
|
||||
MockA!.Error = "overloaded";
|
||||
MockA.Weight = 10_000;
|
||||
MockA.Error = "overloaded";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Waage in Überlast"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_05_InvalidResponse() {
|
||||
MockA!.Weight = 1_000;
|
||||
MockA!.Error = "invalid";
|
||||
MockA.Weight = 1_000;
|
||||
MockA.Error = "invalid";
|
||||
Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_06_InvalidCrc() {
|
||||
MockA!.Weight = 1_000;
|
||||
MockA!.Error = "crc";
|
||||
MockA.Weight = 1_000;
|
||||
MockA.Error = "crc";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||
Assert.That(ex.Message, Contains.Substring("Invalid CRC16 checksum"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_07_InvalidUnit() {
|
||||
MockA!.Weight = 1_000;
|
||||
MockA!.Error = "unit";
|
||||
MockA.Weight = 1_000;
|
||||
MockA.Error = "unit";
|
||||
IOException ex = Assert.ThrowsAsync<IOException>(async () => await ScaleA!.Weigh());
|
||||
}
|
||||
}
|
||||
|
19
Tests/config.test.ini
Normal file
19
Tests/config.test.ini
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
[general]
|
||||
debug = true
|
||||
|
||||
[database]
|
||||
file = ElwigTestDB.sqlite3
|
||||
|
||||
[scale.1]
|
||||
type = SysTec-IT
|
||||
model = IT3000A
|
||||
connection = tcp://127.0.0.1:12345
|
||||
required = false
|
||||
|
||||
[scale.2]
|
||||
type = Avery-Async
|
||||
model = L320
|
||||
connection = tcp://127.0.0.1:12346
|
||||
required = false
|
||||
|
@ -1 +1 @@
|
||||
curl --fail -s -L "https://elwig.at/files/create.sql?v=23" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||
curl --fail -s -L "https://elwig.at/files/create.sql?v=24" -u "elwig:ganzGeheim123!" -o "Resources\Sql\Create.sql"
|
||||
|
Reference in New Issue
Block a user