Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing Package Description truncation #3638

Merged
merged 7 commits into from Mar 9, 2017
Merged

Conversation

ryuyu
Copy link
Contributor

@ryuyu ryuyu commented Mar 9, 2017

Split description truncation from extra information so we can parse html in extra info.
Fix for #3604

@skofman1 @shishirx34 @joelverhagen

{
if (this.Description.Length > _descriptionLengthLimit)
{
return this.Description.Substring(1, _descriptionLengthLimit) + "...";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start index 1?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this does not truncate at word boundary, as the old implementation does. Could we use that Razor helper here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops

{
get
{
if (this.Description.Length > _descriptionLengthLimit)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs unit test

@Model.ShortDescription;
@if (Model.Description.Length > Model.DescriptionLengthLimit)
{
@Html.Raw(string.Format(System.Globalization.CultureInfo.CurrentCulture, " <a href=\"{0}\">More information</a>", @Url.Package(Model)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using the helper for building links:
http://stackoverflow.com/a/7709015/52749

Copy link
Member

@joelverhagen joelverhagen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⌚️

@@ -8,6 +8,8 @@ namespace NuGetGallery
{
public class ListPackageItemViewModel : PackageViewModel
{
private int _descriptionLengthLimit = 350;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private const int

Copy link
Contributor

@scottbommarito scottbommarito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest a solution for this that changes the existing TruncateAtWordBoundary function or creates a new method that doesn't HTML encode the omission string. I think it would be much more elegant and would reuse the existing TruncateAtWordBoundary code.

@skofman1
Copy link
Contributor

skofman1 commented Mar 9, 2017

why do we need any changes to the ViewModel? You can fix this directly in the cshtml..

…ernal structure of ListPacakgeItemViewModel.
@@ -46,12 +46,12 @@
</p>

@if (@Model.Tags.AnySafe())
{
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: fix indentation

Copy link
Member

@joelverhagen joelverhagen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address nit then :shipit:

@joelverhagen
Copy link
Member

Could we have a unit test of the ViewModel?

@joelverhagen
Copy link
Member

why do we need any changes to the ViewModel? You can fix this directly in the cshtml..

https://msdn.microsoft.com/en-us/library/hh848246.aspx

Under The Benefits of MVVM:

  • The developers can create unit tests for the view model and the model without using the view. The unit tests for the view model can exercise exactly the same functionality as used by the view.
  • It is easy to redesign the UI of the application without touching the code because the view is implemented entirely in XAML. A new version of the view should work with the existing view model.

@@ -43,13 +43,15 @@ public static string Abbreviate(this string text, int length)
return text.Substring(0, length - 3) + "...";
}

public static string TruncateAtWordBoundary(this string input, int length = 300, string ommission = "...", string moreText = "")
public static string TruncateAtWordBoundary(this string input, int length, string ommission, string moreText, out bool wasTruncated)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasTruncated isn't necessary. All that needs to be done is to make string moreText not be HTML encoded. I recommend making this method return an MvcHtmlString that you encode yourself. You shouldn't need to change anything but this method for your fix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that I don't mind giving the ViewModel a shortDescription property, it just shouldn't require anything but calling this method directly.

Copy link
Contributor Author

@ryuyu ryuyu Mar 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please read the rest of the discussion before making comments like this. The new model moves logic from the view into the model, and makes this block more testable.

This is also a string extension and should return a string unless the method implies otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regardless of whether you call TruncateAtWordBoundary in _ListPackage.cshtml or in ListPackageItemViewModel, you still don't need to add this additional parameter. The ViewModel can just call TruncateAtWordBoundary with moreText specified as Html.RouteLink("More information", RouteName.DisplayPackage, new { Model.Id, Model.Version }) and modify this method so that it doesn't HTML encode it. Splitting up this logic as you've done is unnecessary.

Additionally, this method previously returned an MvcHtmlString before it was changed to prevent XSS attacks. Its status as a string extension is irrelevant.

Also, I suggest if you want to increase the testability of the block of code with your changes, you should move the @if (Model.IsDescriptionTruncated) from the cshtml into the ViewModel. You can then test that the correct HTML link is added as well as testing that it was truncated correctly.

if (string.IsNullOrEmpty(input) || input.Length < length)
return input;

int nextSpace = input.LastIndexOf(" ", length, StringComparison.Ordinal);

wasTruncated = true;
return string.Format(CultureInfo.CurrentCulture, "{2}{1}{0}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix the ordering of this so it's more readable? The parameters should be ordered so that it's {0}{1}{2}.

{
wasTruncated = false;
if (string.IsNullOrEmpty(input) || input.Length < length)
return input;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add { }
We shouldn't continue with the old coding convention

[Fact]
public void LongDescriptionsTruncated()
{
var description = @"A Longer description full of nonsense that will get truncated. Lorem ipsum dolor sit amet, ad nemore gubergren eam. Ea quaeque labores deseruisse his, eos munere convenire at, in eos audire persius corpora. Te his volumus detracto offendit, has ne illud choro. No illum quaestio mel, novum democritum te sea, et nam nisl officiis salutandi. Vis ut harum docendi incorrupte, nam affert putent sententiae id, mei cibo omnium id. Ea est falli graeci voluptatibus, est mollis denique ne.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can test additional scenarios, like what if it's a single long string with no spaces, etc.

@@ -0,0 +1,214 @@
using System;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like the extra coverage 💯

Owners = package.PackageRegistration?.Owners;

bool wasTruncated;
ShortDescription = Description.TruncateAtWordBoundary(_descriptionLengthLimit, _omissionString, out wasTruncated);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you pass IsDescriptionTruncated as the out bool directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No.

{
// start with replicating the PackageViewModelFacts here since we shouldn't be breaking these
// ListPackageItemViewModel extends PackageViewModel
#region CopiedFromPackageViewModelFacts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a very good approach. The next time somebody changes PackageViewModelFacts, these test cases will become outdated and it won't be obvious that these need to be fixed as well. If you would really like to run these test cases on both classes, you should make each test case in PackageViewModelFacts a theory that takes in either PackageViewModel or ListPackageItemViewModel and then runs on that class.

Additionally, I was under the impression that unit tests for a subclass should test only the functionality that a subclass adds or changes rather than the entirety of the functionality of the superclass. I agree that we don't want to be breaking any of the previous functionality, but I think it's fairly safe to assume that the functionality of the subclass that hasn't been modified should be the same as the superclass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind removing them from here. I just wanted to make sure that i wasn't interfering with any PackageViewModel functionality inadvertently.

That being said, the concern for synchronization between the locations is a valid one. Given that, we should make a decision about the general guidance for testing of derived classes.

  1. Should derived class have tests for testing interference with base class if we aren't explicitly overriding?
  2. Should it be assumes that all child classes are verified when modifying a parent class?
  3. is it a good idea to separate testing by class, or should we test by hierarchy (ie PackageViewModelTests should or should not include tests for all derived classes for it's own functionality)?

This is a good discussion, but outside of the scope of the PR. If there is a particular concern, I'll remove them from here, but if a base test is updated, this one should break when run, so I'm a little less concerned about being "outdated"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to remove them from here. Although they probably will, there isn't any guarantee that these tests will automatically break when we change the others, so maintainability is a major concern for me, as running bad unit tests unknowingly is in some ways just as bad as not testing at all. Additionally, we haven't copied our tests for each subclass anywhere else.

We can determine our process for testing derived classes on a later date and then modify these test cases as we see fit.

@@ -8,20 +9,29 @@ namespace NuGetGallery
{
public class ListPackageItemViewModel : PackageViewModel
{
private const int _descriptionLengthLimit = 300;
private const string _omissionString = "...";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to consider using http://www.fileformat.info/info/unicode/char/2026/index.htm instead of "..."?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. On one hand, I like unicode. On the other hand, the "." character doesn't require unicode. I'm leaning towards leaving it as is.

@ryuyu ryuyu merged commit f3a9d60 into dev Mar 9, 2017
@skofman1 skofman1 mentioned this pull request Mar 28, 2017
jozefizso added a commit to goit/NuGetGallery that referenced this pull request Apr 20, 2017
…7.03.27

* tag 'v2017.03.27': (205 commits)
  Revert UpdateIsLatest optimistic concurrency changes (NuGet#3707)
  UpdateIsLatest concurrent unlist fix (NuGet#3695)
  Change telemetry time to use correct format (NuGet#3690)
  Fix typo of "publically" (NuGet#3636)
  Fix regression (NuGet#3667)
  Add credential to Register and RequestPasswordReset audits (NuGet#3666)
  Functional test for temp keys (NuGet#3664)
  Telemetry for temp keys (NuGet#3662)
  Temp keys implementation (NuGet#3563) (NuGet#3646)
  Extracting code: single type per file (NuGet#3644)
  Telemetry for package push (NuGet#3649)
  Upgrade to NuGet.* v4.0.0 dependencies (NuGet#3643)
  Fix concurrent push test by disabling search hijacking on feed (NuGet#3641)
  Fixing Package Description truncation (NuGet#3638)
  Fix Microsoft Account removal (NuGet#3639)
  Send e-mail when a new API key is created (NuGet#3634)
  IsLatest Fix: wrong connection string passed to retry context (NuGet#3632)
  Update WindowsAzure.Storage to 7.0.0 (NuGet#3633)
  Depend on signed version of Elmah (NuGet#3609)
  Move AzureEntityList and TableErrorLog to NuGetGallery.Core (NuGet#3607)
  ...
@ryuyu ryuyu deleted the ryuyu-fixTruncatedDescriptions branch July 6, 2017 20:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants