{"size": 13002, "ext": "js", "max_stars_repo_path": "client/src/project/Project.test.js", "max_stars_repo_name": "SwissDataScienceCenter/renku-ui", "max_stars_repo_stars_event_min_datetime": "2019-02-07T13:28:26.000Z", "max_stars_repo_stars_event_max_datetime": "2021-10-19T05:26:22.000Z", "max_issues_repo_path": "client/src/project/Project.test.js", "max_issues_repo_name": "SwissDataScienceCenter/renku-ui", "max_issues_repo_issues_event_min_datetime": "2018-05-17T22:13:29.000Z", "max_issues_repo_issues_event_max_datetime": "2022-03-31T11:05:42.000Z", "max_forks_repo_path": "client/src/project/Project.test.js", "max_forks_repo_name": "SwissDataScienceCenter/renku-ui", "max_forks_repo_forks_event_min_datetime": "2018-06-01T08:55:48.000Z", "max_forks_repo_forks_event_max_datetime": "2022-02-17T10:19:52.000Z"}
{"max_stars_count": 7, "max_issues_count": 1255, "max_forks_count": 4, "avg_line_length": 39.045045045, "max_line_length": 118, "alphanum_fraction": 0.6632056607}
/*!
 * Copyright 2017 - Swiss Data Science Center (SDSC)
 * A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
 * Eidgenössische Technische Hochschule Zürich (ETHZ).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 *  incubator-renku-ui
 *
 *  Project.test.js
 *  Tests for project.
 */

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { MemoryRouter } from "react-router-dom";
import TestRenderer, { act } from "react-test-renderer";
import { createMemoryHistory } from "history";

import { StateModel, globalSchema, projectGlobalSchema, SpecialPropVal } from "../model";
import Project, { mapProjectFeatures, withProjectMapped } from "./Project";
import { filterPaths } from "./Project.present";
import { OverviewCommitsBody } from "./overview/ProjectOverview.present";
import { ProjectCoordinator } from "./Project.state";
import { ACCESS_LEVELS, testClient as client } from "../api-client";
import { generateFakeUser } from "../user/User.test";
import { ProjectSuggestActions } from "./Project.present";
import { sleep } from "../utils/HelperFunctions";


const fakeHistory = createMemoryHistory({
  initialEntries: ["/"],
  initialIndex: 0,
});
fakeHistory.push({
  pathname: "/projects",
  search: "?page=1"
});

const getProjectSuggestionProps = (props, loading = true, commits = [], readmeCommits = [], datasets = []) => {
  props.projectCoordinator.get = (ref) => {
    switch (ref) {
      case "commitsReadme":
        return {
          fetching: true,
          fetched: !loading,
          list: readmeCommits,
        };
      case "commits":
        return {
          fetching: true,
          fetched: !loading,
          list: commits,
        };
      case "datasets.core":
        return loading ? SpecialPropVal.UPDATING : { datasets: datasets };
    }
    return {};
  };
  props.projectCoordinator.fetchReadmeCommits = () => readmeCommits;
  props.core = {
    default_branch: "master"
  };
  props.externalUrl = "/gitlab/";
  props.newDatasetUrl = "new-dataset-url";

  return props;
};

describe("test ProjectCoordinator related components", () => {
  const model = new StateModel(globalSchema);
  const projectCoordinator = new ProjectCoordinator(client, model.subModel("project"));
  const projectState = model.subModel("project").get();
  const projectStateKeys = Object.keys(projectState);

  it("test mapProjectFeatures function", () => {
    // mapping all
    let projectMappedState = mapProjectFeatures(projectCoordinator)(model.get());
    expect(projectStateKeys.every(i => Object.keys(projectMappedState).includes(i))).toBeTruthy();
    expect(Object.keys(projectMappedState).every(i => projectStateKeys.includes(i))).toBeTruthy();

    // mapping only one component
    const projectStateKey = projectStateKeys.slice(0, 1);
    projectMappedState = mapProjectFeatures(projectCoordinator, projectStateKey)(model.get());
    expect(projectStateKeys.every(i => Object.keys(projectMappedState).includes(i))).toBeFalsy();
    expect(Object.keys(projectMappedState).every(i => projectStateKeys.includes(i))).toBeTruthy();

    // mapped component may include extra functions
    const projectCategory = "commits";
    const projectCategoryState = model.subModel("project").get(projectCategory);
    let projectCategoryMapped = mapProjectFeatures(projectCoordinator, [projectCategory])(model.get()).commits;
    expect(Object.keys(projectCategoryState).every(i => Object.keys(projectCategoryMapped).includes(i))).toBeTruthy();
    expect(Object.keys(projectCategoryMapped).every(i => Object.keys(projectCategoryState).includes(i))).toBeFalsy();

    // mapped to parent object -- useful to avoid collision when using in legacy components
    const parent = "parent";
    let projectCategoryParent = mapProjectFeatures(projectCoordinator, [projectCategory], parent)(model.get());
    expect(projectCategoryParent[projectCategory]).toBeUndefined();
    expect(projectCategoryParent[parent]).toBeTruthy();
    let descendantKeys = Object.keys(projectCategoryParent[parent][projectCategory]);
    expect(descendantKeys.every(i => Object.keys(projectCategoryMapped).includes(i))).toBeTruthy();
    expect(Object.keys(projectCategoryMapped).every(i => descendantKeys.includes(i))).toBeTruthy();
  });

  it("test withProjectMapped higher order function", () => {
    const div = document.createElement("div");
    const categories = ["commits", "metadata"];
    const CommitsConnected = withProjectMapped(OverviewCommitsBody, categories);

    ReactDOM.render(
      <Provider store={model.reduxStore}>
        <MemoryRouter>
          <CommitsConnected
            history={fakeHistory}
            location={fakeHistory.location}
            projectCoordinator={projectCoordinator} />
        </MemoryRouter>
      </Provider>
      , div);
  });
});

describe("rendering", () => {
  const anonymousUser = generateFakeUser(true);
  const loggedUser = generateFakeUser();
  const model = new StateModel(globalSchema);

  it("renders view without crashing for anonymous user", () => {
    const div = document.createElement("div");
    ReactDOM.render(
      <Provider store={model.reduxStore}>
        <MemoryRouter>
          <Project.View
            id="1"
            client={client}
            user={anonymousUser}
            model={model}
            history={fakeHistory}
            location={fakeHistory.location}
            match={{ params: { id: "1" }, url: "/projects/1/" }} />
        </MemoryRouter>
      </Provider>
      , div);
  });
  it("renders view without crashing for logged user", () => {
    const div = document.createElement("div");
    ReactDOM.render(
      <Provider store={model.reduxStore}>
        <MemoryRouter>
          <Project.View
            id="1"
            client={client}
            model={model}
            history={fakeHistory}
            user={loggedUser}
            location={fakeHistory.location}
            match={{ params: { id: "1" }, url: "/projects/1/" }} />
        </MemoryRouter>
      </Provider>
      , div);
  });
});

describe("new project actions", () => {
  const model = new StateModel(globalSchema);
  const projectCoordinator = new ProjectCoordinator(client, model.subModel("project"));
  it("sets a core field", () => {
    expect(projectCoordinator.get("metadata.title")).toEqual("");
    projectCoordinator.set("metadata.title", "a title");
    expect(projectCoordinator.get("metadata.title")).toEqual("a title");
  });
  it("sets a visibility field", () => {
    expect(projectCoordinator.get("metadata.visibility")).toEqual("private");
    projectCoordinator.set("metadata.visibility", "public");
    expect(projectCoordinator.get("metadata.visibility")).toEqual("public");
  });
});

describe("project view actions", () => {
  it("retrieves a project from server", () => {
    const model = new StateModel(globalSchema);
    const projectCoordinator = new ProjectCoordinator(client, model.subModel("project"));
    // eslint-disable-next-line
    projectCoordinator.fetchProject(client, 1).then(() => {
      expect(projectCoordinator.get("metadata.title")).toEqual("A-first-project");
    });
  });
});

describe("path filtering", () => {
  const origPaths = [".foo", ".renku", ".renku/foo", "foo.txt", "bar",
    "myFolder/.hidden", "myFolder/visible",
    "myFolder/.alsoHidden/readme.md", "myFolder/.alsoHidden/other.txt",
    "myFolder/alsoVisible/.hidden", "myFolder/alsoVisible/readme.md", "myFolder/alsoVisible/other.txt",
  ];
  it(`filters the default blacklist [/^..*/, \\..*/]`, () => {
    const blacklist = [/^\..*/, /\/\..*/];
    const paths = filterPaths(origPaths, blacklist);
    expect(paths).toEqual([
      "foo.txt", "bar", "myFolder/visible", "myFolder/alsoVisible/readme.md", "myFolder/alsoVisible/other.txt"
    ]);
  });

  it(`filters the another blacklist [/^..*/, /readme.md/]`, () => {
    const blacklist = [/^\..*/, /readme.md/];
    const paths = filterPaths(origPaths, blacklist);
    expect(paths).toEqual(["foo.txt", "bar", "myFolder/.hidden", "myFolder/visible",
      "myFolder/.alsoHidden/other.txt",
      "myFolder/alsoVisible/.hidden", "myFolder/alsoVisible/other.txt"]);
  });
});

describe("rendering ProjectSuggestActions", () => {
  const model = new StateModel(globalSchema);
  const projectCoordinator = new ProjectCoordinator(client, model.subModel("project"));

  const props = {
    ...projectGlobalSchema,
    projectCoordinator,
    model: {},
    fetchDatasets: () => {},
    visibility: { accessLevel: ACCESS_LEVELS.MAINTAINER }
  };

  it("Don't render if is loading data", () => {
    const allProps = getProjectSuggestionProps(props, true);
    const component = TestRenderer.create(
      <ProjectSuggestActions key="suggestions" {...allProps} />,
    );
    expect(component.toJSON()).toBe(null);
  });

  it("Don't render if user is not a project maintainer", () => {
    const allProps = getProjectSuggestionProps(props, true);
    allProps.visibility.accessLevel = ACCESS_LEVELS.GUEST;
    const component = TestRenderer.create(
      <ProjectSuggestActions key="suggestions" {...allProps} />,
    );
    expect(component.toJSON()).toBe(null);
    allProps.visibility.accessLevel = ACCESS_LEVELS.MAINTAINER;
  });

  it("only render readme suggestion when exist datasets", async () => {
    const exampleCommit = [{ id: "abc", committed_date: "2021-01-01" }];
    const allProps = getProjectSuggestionProps(props, false, exampleCommit, exampleCommit, [{}]);
    let rendered;
    act(() => {
      rendered = TestRenderer.create(
        <MemoryRouter>
          <ProjectSuggestActions key="suggestions" {...allProps} />
        </MemoryRouter>,
      );
    });
    await sleep(0);
    const testInstance = rendered.root;
    const suggestions = testInstance.findAllByProps({ className: "suggestionTitle" });
    expect(suggestions.length).toBe(1);
    expect(suggestions[0]?.children[0]).toBe("Edit README.md");
  });

  it("only render dataset suggestion when exist more that 1 commit in readme", async () => {
    const exampleCommits = [{ id: "abc", committed_date: "2021-01-01" }, { id: "def", committed_date: "2021-01-02" }];
    const allProps = getProjectSuggestionProps(props, false, exampleCommits, exampleCommits, []);
    let rendered;
    act(() => {
      rendered = TestRenderer.create(
        <MemoryRouter>
          <ProjectSuggestActions key="suggestions" {...allProps} />
        </MemoryRouter>,
      );
    });
    await sleep(0);
    const testInstance = rendered.root;
    const suggestions = testInstance.findAllByProps({ className: "suggestionTitle" });
    expect(suggestions.length).toBe(1);
    expect(suggestions[0]?.children[0]).toBe("Add some datasets");
  });

  it("render all suggestion when exist only that 1 readme commit and no datasets", async () => {
    const exampleCommit = [{ id: "abc", committed_date: "2021-01-01" }];
    const allProps = getProjectSuggestionProps(props, false, exampleCommit, exampleCommit, []);
    let rendered;
    act(() => {
      rendered = TestRenderer.create(
        <MemoryRouter>
          <ProjectSuggestActions key="suggestions" {...allProps} />
        </MemoryRouter>,
      );
    });
    await sleep(0);
    const testInstance = rendered.root;
    const suggestions = testInstance.findAllByProps({ className: "suggestionTitle" });
    expect(suggestions.length).toBe(2);
    expect(suggestions[0]?.children[0]).toBe("Edit README.md");
    expect(suggestions[1]?.children[0]).toBe("Add some datasets");
  });

  it("no render all suggestion when exist more than 4 commits", async () => {
    const exampleCommits = [
      { id: "abc1", committed_date: "2021-01-01" },
      { id: "abc2", committed_date: "2021-01-02" },
      { id: "abc3", committed_date: "2021-01-03" },
      { id: "abc4", committed_date: "2021-01-04" },
      { id: "abc5", committed_date: "2021-01-05" }];
    const exampleReadmeCommit = [{ id: "abc1", committed_date: "2021-01-01" }];
    const allProps = getProjectSuggestionProps(props, false, exampleCommits, exampleReadmeCommit, []);
    let rendered;
    act(() => {
      rendered = TestRenderer.create(
        <MemoryRouter>
          <ProjectSuggestActions key="suggestions" {...allProps} />
        </MemoryRouter>,
      );
    });
    const testInstance = rendered.root;
    const suggestions = testInstance.findAllByProps({ className: "suggestionTitle" });
    expect(suggestions.length).toBe(0);
  });
});
