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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
pull_request:
branches:
- staging

jobs:
feedback-ui-tests:
name: Feedback + UI unit tests (net8.0-windows)
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Run unit tests
run: >
dotnet test ui/Countly.UI.WebView2.Tests/Countly.UI.WebView2.Tests.csproj
-c Release
-p:SignAssembly=false
-p:DelaySign=false
-p:GenerateDocumentationFile=false

core-build:
name: Build core (net35 / net45 / netstd)
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: microsoft/setup-msbuild@v2

- name: Restore + build core targets
shell: pwsh
run: |
nuget restore net35/Countly.sln
nuget restore net45/Countly.sln
msbuild net35/Countly/Countly.csproj /t:Build /p:Configuration=Release /p:SignAssembly=false /p:DelaySign=false /p:GenerateDocumentationFile=false /v:minimal
msbuild net45/Countly/Countly.csproj /t:Build /p:Configuration=Release /p:SignAssembly=false /p:DelaySign=false /p:GenerateDocumentationFile=false /v:minimal
dotnet build netstd/Countly/Countly.csproj -c Release -p:SignAssembly=false -p:DelaySign=false -p:GenerateDocumentationFile=false
61 changes: 61 additions & 0 deletions .github/workflows/release-ui.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Release UI package

# Publishes the Countly.UI.WebView2 NuGet package.
#
# Trigger manually (Actions -> Release UI package -> Run workflow) or by pushing a tag like
# `ui-v26.1.0`. The package version comes from the csproj (<Version>), not the tag.
#
# PREREQUISITES:
# 1. The core `Countly` package at the version referenced by the UI csproj (26.1.0) must ALREADY
# be published to nuget.org — the release build restores it (`-p:UsePackagedCountly=true`).
# 2. Repo secrets:
# - NUGET_API_KEY : nuget.org push key
# - SIGNING_PFX_BASE64 : base64 of CountlyWinSDKStrongNameKey.pfx (strong-name key)
# - SIGNING_PFX_PASSWORD : password for that .pfx (omit/blank if the key is passwordless)

on:
workflow_dispatch:
push:
tags:
- 'ui-v*'

jobs:
release:
name: Pack + push Countly.UI.WebView2
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore strong-name key
shell: pwsh
run: |
$pfx = "netstd/Countly/CountlyWinSDKStrongNameKey.pfx"
[IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String("${{ secrets.SIGNING_PFX_BASE64 }}"))
Write-Host "Wrote signing key to $pfx"

- name: Pack (Release, signed, against the published core)
shell: pwsh
run: >
dotnet pack ui/Countly.UI.WebView2/Countly.UI.WebView2.csproj
-c Release
-p:UsePackagedCountly=true
-p:SignAssembly=true
-p:DelaySign=false
-o ${{ github.workspace }}/artifacts

- name: Push to NuGet
shell: pwsh
run: >
dotnet nuget push "${{ github.workspace }}/artifacts/*.nupkg"
--api-key ${{ secrets.NUGET_API_KEY }}
--source https://api.nuget.org/v3/index.json
--skip-duplicate

- uses: actions/upload-artifact@v4
with:
name: nupkg
path: ${{ github.workspace }}/artifacts/*.nupkg
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,5 @@ _Pvt_Extensions
/nuget/lib/*
nuget/nuget.exe

.DS_Store
.DS_Store
docs/
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
* After initialization finishes.
* RemoteConfig consent is given.
* After the device ID is changed to a different user.
* Added support for the Feedback Widgets feature (Surveys, NPS, Ratings) accessible through the "Countly.Instance.Feedback()" interface:
* "GetAvailableFeedbackWidgets" for fetching the list of available feedback widgets from the server
* "GetFeedbackWidgetData" for fetching a widget's definition (for building a custom UI)
* "ReportFeedbackWidgetManually" for reporting a widget result, or marking it closed
* "ConstructFeedbackWidgetUrl" for building a widget's display URL
* This feature uses "Feedback" consent (and "StarRating" consent for rating widgets).
* Needs "Countly.UI.WebView2" for displaying feedback widgets on WPF and WinForms.
* Added support for the experimental Content feature accessible through the "Countly.Instance.Content()" interface:
* "EnterContentZone" / "ExitContentZone" for starting and stopping periodic content fetching
* "RefreshContentZone" for forcing an immediate refresh
* "PreviewContent" for fetching and showing a specific content by id
* Added "ContentZoneTimerInterval" and a global content callback to the configuration object
* This feature uses "Content" consent.
* Needs "Countly.UI.WebView2" for displaying content on WPF.
* Fixed a bug where the request queue was not processed when automatic user property saving ("autoSendUserDetails") was disabled, so queued requests (for example from a manual session update) were never uploaded and could cause the SDK to loop indefinitely.

## 25.4.3
* ! Minor breaking change ! User properties will now be automatically saved under the following conditions:
Expand Down
9 changes: 6 additions & 3 deletions countlyCommon/TestingRelated/ConsentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public void TestConsentRequest()
Assert.False(string.IsNullOrEmpty(collection.Get("t")));
JObject consentObj = JObject.Parse(collection.Get("consent"));

Assert.Equal(10, consentObj.Count);
Assert.Equal(11, consentObj.Count);
Assert.False(consentObj.GetValue("push").ToObject<bool>());
Assert.True(consentObj.GetValue("users").ToObject<bool>());
Assert.False(consentObj.GetValue("views").ToObject<bool>());
Expand All @@ -206,6 +206,7 @@ public void TestConsentRequest()
Assert.False(consentObj.GetValue("feedback").ToObject<bool>());
Assert.False(consentObj.GetValue("star-rating").ToObject<bool>());
Assert.False(consentObj.GetValue("remote-config").ToObject<bool>());
Assert.False(consentObj.GetValue("content").ToObject<bool>());
}

/// <summary>
Expand Down Expand Up @@ -241,7 +242,7 @@ public void TestGiveIndividualConsents()
NameValueCollection collection = HttpUtility.ParseQueryString(request.Request);
JObject consentObj = JObject.Parse(collection.Get("consent"));

Assert.Equal(10, consentObj.Count);
Assert.Equal(11, consentObj.Count);
Assert.False(consentObj.GetValue("push").ToObject<bool>());
Assert.True(consentObj.GetValue("users").ToObject<bool>());
Assert.False(consentObj.GetValue("views").ToObject<bool>());
Expand All @@ -252,6 +253,7 @@ public void TestGiveIndividualConsents()
Assert.False(consentObj.GetValue("feedback").ToObject<bool>());
Assert.False(consentObj.GetValue("star-rating").ToObject<bool>());
Assert.False(consentObj.GetValue("remote-config").ToObject<bool>());
Assert.False(consentObj.GetValue("content").ToObject<bool>());

Dictionary<ConsentFeatures, bool> consentToRemove = new Dictionary<ConsentFeatures, bool>();
consentToRemove.Add(ConsentFeatures.Crashes, false);
Expand All @@ -265,7 +267,7 @@ public void TestGiveIndividualConsents()
collection = HttpUtility.ParseQueryString(request.Request);
consentObj = JObject.Parse(collection.Get("consent"));

Assert.Equal(10, consentObj.Count);
Assert.Equal(11, consentObj.Count);
Assert.False(consentObj.GetValue("push").ToObject<bool>());
Assert.True(consentObj.GetValue("users").ToObject<bool>());
Assert.False(consentObj.GetValue("views").ToObject<bool>());
Expand All @@ -276,6 +278,7 @@ public void TestGiveIndividualConsents()
Assert.False(consentObj.GetValue("feedback").ToObject<bool>());
Assert.False(consentObj.GetValue("star-rating").ToObject<bool>());
Assert.False(consentObj.GetValue("remote-config").ToObject<bool>());
Assert.False(consentObj.GetValue("content").ToObject<bool>());
}
}
}
3 changes: 2 additions & 1 deletion countlyCommon/TestingRelated/DeviceIdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public async void TestConsent_ChangeDeviceIdWithoutMerge()
collection = HttpUtility.ParseQueryString(request.Request);
JObject consentObj = JObject.Parse(collection.Get("consent"));

Assert.Equal(10, consentObj.Count);
Assert.Equal(11, consentObj.Count);
Assert.False(consentObj.GetValue("push").ToObject<bool>());
Assert.True(consentObj.GetValue("users").ToObject<bool>());
Assert.False(consentObj.GetValue("views").ToObject<bool>());
Expand All @@ -152,6 +152,7 @@ public async void TestConsent_ChangeDeviceIdWithoutMerge()
Assert.False(consentObj.GetValue("feedback").ToObject<bool>());
Assert.False(consentObj.GetValue("star-rating").ToObject<bool>());
Assert.False(consentObj.GetValue("remote-config").ToObject<bool>());
Assert.False(consentObj.GetValue("content").ToObject<bool>());

type = collection.Get("t");
Assert.False(string.IsNullOrEmpty(type));
Expand Down
8 changes: 6 additions & 2 deletions countlyCommon/TestingRelated/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,16 @@ internal static void ValidateRequestInQueue(string deviceId, string appKey, IDic
}
}

internal static void ValidateRequest(Dictionary<string, string> request, IDictionary<string, object> paramaters)
internal static void ValidateRequest(Dictionary<string, string> request, IDictionary<string, object> paramaters, IDictionary<string, Action<string, object>> customValidators = null)
{
ValidateBaseParams(request, DEVICE_ID, APP_KEY, 0);
Assert.Equal(11 + paramaters.Count, request.Count); // + rr
foreach (KeyValuePair<string, object> item in paramaters) {
Assert.Equal(item.Value.ToString(), request[item.Key]);
if (customValidators != null && customValidators.ContainsKey(item.Key)) {
customValidators[item.Key].Invoke(request[item.Key], item.Value);
} else {
Assert.Equal(item.Value.ToString(), request[item.Key]);
}
}
}

Expand Down
8 changes: 7 additions & 1 deletion countlyCommon/TestingRelated/UserDetailsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,13 @@ public void SetUserDetails_SessionEventsTriggers()
TestHelper.ValidateRequest(server.Requests[3].Params, TestHelper.Dict("user_details", TestHelper.Json("name", "John")));
Assert.Contains("Test1", server.Requests[4].Params["events"]);
TestHelper.ValidateRequest(server.Requests[5].Params, TestHelper.Dict("user_details", TestHelper.Json("email", "Doe@doe.com")));
TestHelper.ValidateRequest(server.Requests[6].Params, TestHelper.Dict("end_session", "1", "session_duration", 3));
// session_duration here is wall-clock derived (~2.4s of sleeps), so it rounds to 2 or 3
// depending on machine/CI speed. Validate its presence with a tolerant range instead of
// an exact value to avoid timing flakiness.
TestHelper.ValidateRequest(server.Requests[6].Params, TestHelper.Dict("end_session", "1", "session_duration", 3),
new Dictionary<string, Action<string, object>> {
{ "session_duration", (actual, _) => Assert.True(int.Parse(actual) >= 2 && int.Parse(actual) <= 4, "session_duration was " + actual) }
});

server.Dispose();
}
Expand Down
Loading
Loading