<commit_before>/* global describe, it, expect, jest */
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import { createStore } from 'redux';
import * as reactRedux from 'react-redux';

jest.mock('../../src/components/post-comments', () => ({ comments }) => {
  return
{comments.length > 0 ? `Mocked ${comments.length} comments ` : ''}
;
});

jest.mock('../../src/components/post-attachments', () => ({ attachments }) => {
  return
{attachments.length > 0 ? `Mocked ${attachments.length} attachments` : ''}
;
});

// https://github.com/facebook/jest/issues/8769#issuecomment-812824244
jest.mock('../../src/components/lazy-component', () => ({
  lazyComponent: (loader, { fallback, errorMessage }) => () => {
    return
{fallback || errorMessage}
;
  },
}));

import Post from '../../src/components/post';

const AUTHOR = {
  id: 'author-id',
  username: 'author',
  screenName: 'Author',
  type: 'user',
  isPrivate: '0',
  isProtected: '0',
};

const VIEWER = {
  ...AUTHOR,
  frontendPreferences: {
    timeDisplay: {},
  },
};

const defaultState = {
  user: VIEWER,
  postHideStatuses: {},
  sendTo: { feeds: [{ id: 'feed-id', user: AUTHOR }] },
};

const renderPost = (props = {}, options = {}) => {
  const { Provider } = reactRedux;
  const dummyReducer = (state) => state;
  const store = createStore(dummyReducer, defaultState);

  const defaultProps = {
    id: 'post-id',
    body: 'Line 1\nLine 2',
    createdAt: '1615692239035',
    updatedAt: '1615692257002',
    comments: [],
    attachments: [],
    likes: [],
    createdBy: AUTHOR,
    recipients: [AUTHOR],
    recipientNames: [AUTHOR.username],
    isSinglePost: true,
    isDirect: false,
    isNSFW: false,
    commentsDisabled: false,
    commentLikes: 0,
    ownCommentLikes: 0,
    omittedCommentLikes: 0,
    omittedOwnCommentLikes: 0,
    omittedComments: 0,
    omittedLikes: 0,
    usersLikedPost: [],
    user: VIEWER,
    savePostStatus: {},
    hideStatus: {},
    setFinalHideLinkOffset: () => {},
    toggleEditingPost: () => {},
    saveEditingPost: () => {},
  };

  const rendered = render(
    <Provider store={store}>
      <Post {...defaultProps} {...props} />
    </Provider>,
    options,
  );

  return {
    ...rendered,
    rerender: (props = {}, options = {}) =>
      renderPost(props, { container: rendered.container, ...options }),
  };
};

describe('Post', () => {
  it("Renders a post and doesn't blow up", () => {
    const { asFragment } = renderPost();
    expect(asFragment()).toMatchSnapshot();
  });

  it('Displays all post recipients', () => {
    const someOtherGuy = {
      id: 'other-guy-id',
      username: 'otherguy1',
      screenName: 'Other Guy',
      type: 'user',
    };
    const someOtherGroup = {
      id: 'other-group-id',
      username: 'othergroup',
      screenName: 'Other Group',
      type: 'group',
    };
    renderPost({
      recipients: [AUTHOR, someOtherGuy, someOtherGroup],
      recipientNames: [AUTHOR.username, someOtherGuy.username, someOtherGroup.username],
    });
    expect(screen.getByLabelText('Post body')).toHaveTextContent(
      'You to author’s feed, Other Guy and Other Group',
    );
  });

  it('Displays post likes', () => {
    renderPost({
      likes: ['u1', 'u2', 'u3'],
      usersLikedPost: [
        { id: 'u1', username: 'user1', screenName: 'User 1' },
        { id: 'u2', username: 'user2', screenName: 'User 2' },
        { id: 'u3', username: 'user3', screenName: 'User 3' },
      ],
    });
    expect(screen.getByLabelText('3 likes')).toHaveTextContent(
      'User 1, User 2 and User 3 liked this',
    );
  });

  it('Displays post comments', () => {
    renderPost({
      comments: [{ id: 'c1' }, { id: 'c2' }],
    });
    expect(screen.getByText(/Mocked 2 comments/)).toBeInTheDocument();
  });

  it('Displays post attachments', () => {
    renderPost({
      attachments: [{ id: 'a1' }, { id: 'a2' }],
    });
    expect(screen.getByText('Mocked 2 attachments')).toBeInTheDocument();
  });

  it('Displays NSFW warning for a NSFW post with image attachments', () => {
    renderPost({
      isNSFW: true,
      attachments: [{ id: 'a1', mediaType: 'image' }, { id: 'a2' }],
    });
    expect(screen.getByLabelText(/Not safe for work Public post/)).toHaveTextContent(
      /Turn the NSFW filter off to enable previews for sensitive content/,
    );
  });

  it('Displays NSFW warning for a NSFW post with a link embed', () => {
    renderPost({
      isNSFW: true,
      body: 'embed https://example.com/',
    });
    expect(screen.getByLabelText(/Not safe for work Public post/)).toHaveTextContent(
      /Turn the NSFW filter off to enable previews for sensitive content/,
    );
  });

  it('Displays a link embed for a normal post with a link embed', () => {
    renderPost({
      body: 'embed https://example.com/',
      allowLinksPreview: false,
    });
    expect(screen.getByLabelText(/Link preview/)).toBeInTheDocument();
  });

  it('Renders a post as public and toggles timestamps if post visibility icon is clicked', () => {
    renderPost();
    expect(screen.getByLabelText(/Public post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is public/, { role: 'button' })).toBeInTheDocument();
    expect(screen.getByText('Mar 14, 2021')).toBeInTheDocument();
    userEvent.click(screen.getByTitle(/This entry is public/, { role: 'button' }));
    expect(screen.getByText('Mar 14, 2021 03:23')).toBeInTheDocument();
  });

  it('Renders a post as protected because its author is protected', () => {
    const protectedAuthor = {
      ...AUTHOR,
      isProtected: '1',
    };
    renderPost({
      recipients: [protectedAuthor],
      createdBy: protectedAuthor,
    });
    expect(screen.getByLabelText(/Protected post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is only visible to FreeFeed users/)).toBeInTheDocument();
  });

  it('Renders a post as protected because all of its recipients are protected', () => {
    const protectedGroup = {
      id: 'pg1',
      username: 'protected-group',
      type: 'group',
      isProtected: '1',
    };
    const protectedAuthor = {
      ...AUTHOR,
      isProtected: '1',
    };
    renderPost({
      recipients: [protectedAuthor, protectedGroup],
      createdBy: protectedAuthor,
    });
    expect(screen.getByLabelText(/Protected post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is only visible to FreeFeed users/)).toBeInTheDocument();
  });

  it('Renders a post as private because its author is private', () => {
    const privateAuthor = {
      ...AUTHOR,
      isPrivate: '1',
    };
    renderPost({
      recipients: [privateAuthor],
      createdBy: privateAuthor,
    });
    expect(screen.getByLabelText(/Private post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is private/)).toBeInTheDocument();
  });

  it('Renders a post as a direct message', () => {
    renderPost({
      isDirect: true,
    });
    expect(screen.getByLabelText(/Direct message/)).toBeInTheDocument();
    expect(screen.getByTitle(/This is a direct message/)).toBeInTheDocument();
  });

  it('Renders a comment button which opens the comment form if this is not a single post', () => {
    const toggleCommenting = jest.fn();
    renderPost({ toggleCommenting, isSinglePost: false });
    expect(screen.getByText('Comment', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Comment', { role: 'button' }));
    expect(toggleCommenting).toHaveBeenCalledWith('post-id');
  });

  it("Doesn't render a comment button if comments are disabled", () => {
    renderPost({ commentsDisabled: true });
    expect(screen.queryByText('Comment', { role: 'button' })).not.toBeInTheDocument();
    expect(screen.getByText('Comments disabled')).toBeInTheDocument();
  });

  it('Renders a comment button if comments are disabled but you are the moderator/owner', () => {
    renderPost({ commentsDisabled: true, isEditable: true });
    expect(screen.getByText('Comment', { role: 'button' })).toBeInTheDocument();
    expect(screen.getByText('Comments disabled (not for you)')).toBeInTheDocument();
  });

  it('Renders a like button which likes the post', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const likePost = jest.fn();
    renderPost({ likePost, isEditable: false, user: someOtherUser });
    expect(screen.getByText('Like', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Like', { role: 'button' }));
    expect(likePost).toHaveBeenCalledWith('post-id', 'other-id');
  });

  it('Renders an un-like button which un-likes the post if this post is already liked', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const unlikePost = jest.fn();
    renderPost({
      unlikePost,
      isEditable: false,
      usersLikedPost: [someOtherUser],
      user: someOtherUser,
    });
    expect(screen.getByText('Un-like', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Un-like', { role: 'button' }));
    expect(unlikePost).toHaveBeenCalledWith('post-id', 'other-id');
  });

  it("Doesn't render a like/unlike button if this is a post that I can edit (e.g. my own post)", () => {
    renderPost({ isEditable: true });
    expect(screen.queryByText('Like', { role: 'button' })).not.toBeInTheDocument();
    expect(screen.queryByText('Un-like', { role: 'button' })).not.toBeInTheDocument();
  });

  it('Renders a more button if this is a post that I can edit (e.g. my own post) which toggles a "more action" menu', () => {
    const confirmMock = jest.spyOn(global, 'confirm').mockReturnValueOnce(true);

    const toggleEditingPost = jest.fn();
    const toggleModeratingComments = jest.fn();
    const disableComments = jest.fn();
    const deletePost = jest.fn();
    renderPost({
      isEditable: true,
      isModeratable: true,
      isFullyRemovable: true,
      toggleEditingPost,
      toggleModeratingComments,
      disableComments,
      deletePost,
    });
    expect(screen.getByText(/More/, { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText(/More/, { role: 'button' }));

    expect(screen.getByText('Edit', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Edit', { role: 'button' }));
    expect(toggleEditingPost).toHaveBeenCalledWith('post-id');

    expect(screen.getByText('Moderate comments', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Moderate comments', { role: 'button' }));
    expect(toggleModeratingComments).toHaveBeenCalledWith('post-id');

    expect(screen.getByText('Disable comments', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Disable comments', { role: 'button' }));
    expect(disableComments).toHaveBeenCalledWith('post-id');

    expect(screen.getByText('Delete', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Delete', { role: 'button' }));
    expect(confirmMock).toHaveBeenCalledWith('Are you sure?');
    expect(deletePost).toHaveBeenCalledWith('post-id');
  });

  it('Renders a hide button which hides the post', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const hidePost = jest.fn();
    renderPost({
      user: someOtherUser,
      isInHomeFeed: true,
      isHidden: false,
      hidePost,
    });
    expect(screen.getByText('Hide', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Hide', { role: 'button' }));
    expect(hidePost).toHaveBeenCalledWith('post-id');
  });

  it('Renders a un-hide button which unhides the post', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const unhidePost = jest.fn();
    renderPost({
      user: someOtherUser,
      isInHomeFeed: true,
      isHidden: true,
      hiddenByNames: false,
      unhidePost,
    });
    expect(screen.getByText('Un-hide', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Un-hide', { role: 'button' }));
    expect(unhidePost).toHaveBeenCalledWith('post-id');
  });

  it('Renders a textarea with post text when editing the post', () => {
    const cancelEditingPost = jest.fn();
    const { rerender } = renderPost({ isEditable: true, cancelEditingPost });

    userEvent.click(screen.getByText(/More/, { role: 'button' }));
    userEvent.click(screen.getByText('Edit', { role: 'button' }));
    rerender({ isEditing: true, isEditable: true, cancelEditingPost });

    expect(screen.getByLabelText('Post body')).toMatchSnapshot();

    userEvent.click(screen.getByText('Cancel'));
    expect(cancelEditingPost).toHaveBeenCalledWith('post-id');
  });

  it('Lets me edit text of my post by typing', () => {
    const saveEditingPost = jest.fn();
    renderPost({ isEditing: true, isEditable: true, saveEditingPost });

    userEvent.type(screen.getByRole('textbox'), 'Hello,{shift}{enter}{/shift}World!{enter}');
    expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!');
    expect(saveEditingPost).toHaveBeenCalledWith('post-id', {
      attachments: [],
      body: 'Hello,\nWorld!',
      feeds: ['author'],
    });
  });
});
<commit_msg>Add a test for enabling post comments<commit_after>/* global describe, it, expect, jest */
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect';
import { createStore } from 'redux';
import * as reactRedux from 'react-redux';

jest.mock('../../src/components/post-comments', () => ({ comments }) => {
  return
{comments.length > 0 ? `Mocked ${comments.length} comments ` : ''}
;
});

jest.mock('../../src/components/post-attachments', () => ({ attachments }) => {
  return
{attachments.length > 0 ? `Mocked ${attachments.length} attachments` : ''}
;
});

// https://github.com/facebook/jest/issues/8769#issuecomment-812824244
jest.mock('../../src/components/lazy-component', () => ({
  lazyComponent: (loader, { fallback, errorMessage }) => () => {
    return
{fallback || errorMessage}
;
  },
}));

import Post from '../../src/components/post';

const AUTHOR = {
  id: 'author-id',
  username: 'author',
  screenName: 'Author',
  type: 'user',
  isPrivate: '0',
  isProtected: '0',
};

const VIEWER = {
  ...AUTHOR,
  frontendPreferences: {
    timeDisplay: {},
  },
};

const defaultState = {
  user: VIEWER,
  postHideStatuses: {},
  sendTo: { feeds: [{ id: 'feed-id', user: AUTHOR }] },
};

const renderPost = (props = {}, options = {}) => {
  const { Provider } = reactRedux;
  const dummyReducer = (state) => state;
  const store = createStore(dummyReducer, defaultState);

  const defaultProps = {
    id: 'post-id',
    body: 'Line 1\nLine 2',
    createdAt: '1615692239035',
    updatedAt: '1615692257002',
    comments: [],
    attachments: [],
    likes: [],
    createdBy: AUTHOR,
    recipients: [AUTHOR],
    recipientNames: [AUTHOR.username],
    isSinglePost: true,
    isDirect: false,
    isNSFW: false,
    commentsDisabled: false,
    commentLikes: 0,
    ownCommentLikes: 0,
    omittedCommentLikes: 0,
    omittedOwnCommentLikes: 0,
    omittedComments: 0,
    omittedLikes: 0,
    usersLikedPost: [],
    user: VIEWER,
    savePostStatus: {},
    hideStatus: {},
    setFinalHideLinkOffset: () => {},
    toggleEditingPost: () => {},
    saveEditingPost: () => {},
  };

  const rendered = render(
    <Provider store={store}>
      <Post {...defaultProps} {...props} />
    </Provider>,
    options,
  );

  return {
    ...rendered,
    rerender: (props = {}, options = {}) =>
      renderPost(props, { container: rendered.container, ...options }),
  };
};

describe('Post', () => {
  it("Renders a post and doesn't blow up", () => {
    const { asFragment } = renderPost();
    expect(asFragment()).toMatchSnapshot();
  });

  it('Displays all post recipients', () => {
    const someOtherGuy = {
      id: 'other-guy-id',
      username: 'otherguy1',
      screenName: 'Other Guy',
      type: 'user',
    };
    const someOtherGroup = {
      id: 'other-group-id',
      username: 'othergroup',
      screenName: 'Other Group',
      type: 'group',
    };
    renderPost({
      recipients: [AUTHOR, someOtherGuy, someOtherGroup],
      recipientNames: [AUTHOR.username, someOtherGuy.username, someOtherGroup.username],
    });
    expect(screen.getByLabelText('Post body')).toHaveTextContent(
      'You to author’s feed, Other Guy and Other Group',
    );
  });

  it('Displays post likes', () => {
    renderPost({
      likes: ['u1', 'u2', 'u3'],
      usersLikedPost: [
        { id: 'u1', username: 'user1', screenName: 'User 1' },
        { id: 'u2', username: 'user2', screenName: 'User 2' },
        { id: 'u3', username: 'user3', screenName: 'User 3' },
      ],
    });
    expect(screen.getByLabelText('3 likes')).toHaveTextContent(
      'User 1, User 2 and User 3 liked this',
    );
  });

  it('Displays post comments', () => {
    renderPost({
      comments: [{ id: 'c1' }, { id: 'c2' }],
    });
    expect(screen.getByText(/Mocked 2 comments/)).toBeInTheDocument();
  });

  it('Displays post attachments', () => {
    renderPost({
      attachments: [{ id: 'a1' }, { id: 'a2' }],
    });
    expect(screen.getByText('Mocked 2 attachments')).toBeInTheDocument();
  });

  it('Displays NSFW warning for a NSFW post with image attachments', () => {
    renderPost({
      isNSFW: true,
      attachments: [{ id: 'a1', mediaType: 'image' }, { id: 'a2' }],
    });
    expect(screen.getByLabelText(/Not safe for work Public post/)).toHaveTextContent(
      /Turn the NSFW filter off to enable previews for sensitive content/,
    );
  });

  it('Displays NSFW warning for a NSFW post with a link embed', () => {
    renderPost({
      isNSFW: true,
      body: 'embed https://example.com/',
    });
    expect(screen.getByLabelText(/Not safe for work Public post/)).toHaveTextContent(
      /Turn the NSFW filter off to enable previews for sensitive content/,
    );
  });

  it('Displays a link embed for a normal post with a link embed', () => {
    renderPost({
      body: 'embed https://example.com/',
      allowLinksPreview: false,
    });
    expect(screen.getByLabelText(/Link preview/)).toBeInTheDocument();
  });

  it('Renders a post as public and toggles timestamps if post visibility icon is clicked', () => {
    renderPost();
    expect(screen.getByLabelText(/Public post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is public/, { role: 'button' })).toBeInTheDocument();
    expect(screen.getByText('Mar 14, 2021')).toBeInTheDocument();
    userEvent.click(screen.getByTitle(/This entry is public/, { role: 'button' }));
    expect(screen.getByText('Mar 14, 2021 03:23')).toBeInTheDocument();
  });

  it('Renders a post as protected because its author is protected', () => {
    const protectedAuthor = {
      ...AUTHOR,
      isProtected: '1',
    };
    renderPost({
      recipients: [protectedAuthor],
      createdBy: protectedAuthor,
    });
    expect(screen.getByLabelText(/Protected post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is only visible to FreeFeed users/)).toBeInTheDocument();
  });

  it('Renders a post as protected because all of its recipients are protected', () => {
    const protectedGroup = {
      id: 'pg1',
      username: 'protected-group',
      type: 'group',
      isProtected: '1',
    };
    const protectedAuthor = {
      ...AUTHOR,
      isProtected: '1',
    };
    renderPost({
      recipients: [protectedAuthor, protectedGroup],
      createdBy: protectedAuthor,
    });
    expect(screen.getByLabelText(/Protected post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is only visible to FreeFeed users/)).toBeInTheDocument();
  });

  it('Renders a post as private because its author is private', () => {
    const privateAuthor = {
      ...AUTHOR,
      isPrivate: '1',
    };
    renderPost({
      recipients: [privateAuthor],
      createdBy: privateAuthor,
    });
    expect(screen.getByLabelText(/Private post/)).toBeInTheDocument();
    expect(screen.getByTitle(/This entry is private/)).toBeInTheDocument();
  });

  it('Renders a post as a direct message', () => {
    renderPost({
      isDirect: true,
    });
    expect(screen.getByLabelText(/Direct message/)).toBeInTheDocument();
    expect(screen.getByTitle(/This is a direct message/)).toBeInTheDocument();
  });

  it('Renders a comment button which opens the comment form if this is not a single post', () => {
    const toggleCommenting = jest.fn();
    renderPost({ toggleCommenting, isSinglePost: false });
    expect(screen.getByText('Comment', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Comment', { role: 'button' }));
    expect(toggleCommenting).toHaveBeenCalledWith('post-id');
  });

  it("Doesn't render a comment button if comments are disabled", () => {
    renderPost({ commentsDisabled: true });
    expect(screen.queryByText('Comment', { role: 'button' })).not.toBeInTheDocument();
    expect(screen.getByText('Comments disabled')).toBeInTheDocument();
  });

  it('Renders a comment button if comments are disabled but you are the moderator/owner', () => {
    renderPost({ commentsDisabled: true, isEditable: true });
    expect(screen.getByText('Comment', { role: 'button' })).toBeInTheDocument();
    expect(screen.getByText('Comments disabled (not for you)')).toBeInTheDocument();
  });

  it('Renders a like button which likes the post', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const likePost = jest.fn();
    renderPost({ likePost, isEditable: false, user: someOtherUser });
    expect(screen.getByText('Like', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Like', { role: 'button' }));
    expect(likePost).toHaveBeenCalledWith('post-id', 'other-id');
  });

  it('Renders an un-like button which un-likes the post if this post is already liked', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const unlikePost = jest.fn();
    renderPost({
      unlikePost,
      isEditable: false,
      usersLikedPost: [someOtherUser],
      user: someOtherUser,
    });
    expect(screen.getByText('Un-like', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Un-like', { role: 'button' }));
    expect(unlikePost).toHaveBeenCalledWith('post-id', 'other-id');
  });

  it("Doesn't render a like/unlike button if this is a post that I can edit (e.g. my own post)", () => {
    renderPost({ isEditable: true });
    expect(screen.queryByText('Like', { role: 'button' })).not.toBeInTheDocument();
    expect(screen.queryByText('Un-like', { role: 'button' })).not.toBeInTheDocument();
  });

  it('Renders a more button if this is a post that I can edit (e.g. my own post) which toggles a "more action" menu', () => {
    const confirmMock = jest.spyOn(global, 'confirm').mockReturnValueOnce(true);

    const toggleEditingPost = jest.fn();
    const toggleModeratingComments = jest.fn();
    const disableComments = jest.fn();
    const deletePost = jest.fn();
    renderPost({
      isEditable: true,
      isModeratable: true,
      isFullyRemovable: true,
      toggleEditingPost,
      toggleModeratingComments,
      disableComments,
      deletePost,
    });
    expect(screen.getByText(/More/, { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText(/More/, { role: 'button' }));

    expect(screen.getByText('Edit', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Edit', { role: 'button' }));
    expect(toggleEditingPost).toHaveBeenCalledWith('post-id');

    expect(screen.getByText('Moderate comments', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Moderate comments', { role: 'button' }));
    expect(toggleModeratingComments).toHaveBeenCalledWith('post-id');

    expect(screen.getByText('Disable comments', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Disable comments', { role: 'button' }));
    expect(disableComments).toHaveBeenCalledWith('post-id');

    expect(screen.getByText('Delete', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Delete', { role: 'button' }));
    expect(confirmMock).toHaveBeenCalledWith('Are you sure?');
    expect(deletePost).toHaveBeenCalledWith('post-id');
  });

  it('Lets me enable comments under my post if they are disabled', () => {
    const enableComments = jest.fn();
    renderPost({
      isEditable: true,
      commentsDisabled: true,
      enableComments,
    });
    expect(screen.getByText(/More/, { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText(/More/, { role: 'button' }));

    expect(screen.getByText('Enable comments', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Enable comments', { role: 'button' }));
    expect(enableComments).toHaveBeenCalledWith('post-id');
  });

  it('Renders a hide button which hides the post', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const hidePost = jest.fn();
    renderPost({
      user: someOtherUser,
      isInHomeFeed: true,
      isHidden: false,
      hidePost,
    });
    expect(screen.getByText('Hide', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Hide', { role: 'button' }));
    expect(hidePost).toHaveBeenCalledWith('post-id');
  });

  it('Renders a un-hide button which unhides the post', () => {
    const someOtherUser = {
      id: 'other-id',
    };
    const unhidePost = jest.fn();
    renderPost({
      user: someOtherUser,
      isInHomeFeed: true,
      isHidden: true,
      hiddenByNames: false,
      unhidePost,
    });
    expect(screen.getByText('Un-hide', { role: 'button' })).toBeInTheDocument();
    userEvent.click(screen.getByText('Un-hide', { role: 'button' }));
    expect(unhidePost).toHaveBeenCalledWith('post-id');
  });

  it('Renders a textarea with post text when editing the post', () => {
    const cancelEditingPost = jest.fn();
    const { rerender } = renderPost({ isEditable: true, cancelEditingPost });

    userEvent.click(screen.getByText(/More/, { role: 'button' }));
    userEvent.click(screen.getByText('Edit', { role: 'button' }));
    rerender({ isEditing: true, isEditable: true, cancelEditingPost });

    expect(screen.getByLabelText('Post body')).toMatchSnapshot();

    userEvent.click(screen.getByText('Cancel'));
    expect(cancelEditingPost).toHaveBeenCalledWith('post-id');
  });

  it('Lets me edit text of my post by typing', () => {
    const saveEditingPost = jest.fn();
    renderPost({ isEditing: true, isEditable: true, saveEditingPost });

    userEvent.type(screen.getByRole('textbox'), 'Hello,{shift}{enter}{/shift}World!{enter}');
    expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!');
    expect(saveEditingPost).toHaveBeenCalledWith('post-id', {
      attachments: [],
      body: 'Hello,\nWorld!',
      feeds: ['author'],
    });
  });
});
<|endoftext|>


<commit_before>var add = require('../.');
describe('medium integers', function () {
	describe('in positive range', function () {
		it('works with numbers', function (done) {
			add(1000, 1000, function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('2000', '1000+1000 should be "2000"');
				done();
			});
		});
		it('works with strings', function (done) {
			add('1000', '1000', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('2000', '1000+1000 should be "2000"');
				done();
			});
		});
		it('works with carry', function (done) {
			add('5099', '5099', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('10198', '5099+5099 should be "10198"');
				done();
			});
		});
		it('works with different length of strings', function (done) {
			add('999', '9999', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('10998', '999+9999 should be "10998"');
				done();
			});
		});
	});
});
<commit_msg>fractions<commit_after>var add = require('../.');
describe('medium integers', function () {
	describe('in positive range', function () {
		it('works with numbers', function (done) {
			add(1000, 1000, function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('2000', '1000+1000 should be "2000"');
				done();
			});
		});
		it('works with strings', function (done) {
			add('1000', '1000', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('2000', '1000+1000 should be "2000"');
				done();
			});
		});
		it('works with carry I.', function (done) {
			add('55', '55', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('110', '55+55 should be "110"');
				done();
			});
		});
		it('works with carry II.', function (done) {
			add('5099', '5099', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('10198', '5099+5099 should be "10198"');
				done();
			});
		});
		it('works with different length of strings', function (done) {
			add('999', '9999', function (err, result) {
				expect(err).toEqual(null, 'medium integers are valid');
				expect(result).toEqual('10998', '999+9999 should be "10998"');
				done();
			});
		});
	});
});
<|endoftext|>


<commit_before>require("./global-leakage.js")
var fs = require('fs')
var test = require('tap').test;
var glob = require('../');

var cwd = process.cwd()
var drive = 'c'
if (/^[a-zA-Z]:[\\\/]/.test(cwd)) {
  drive = cwd.charAt(0).toLowerCase()
}

test('mock fs', function(t) {
  var stat = fs.stat
  var statSync = fs.statSync
  var readdir = fs.readdir
  var readdirSync = fs.readdirSync

  function fakeStat(path) {
    var ret
    switch (path.toLowerCase().replace(/\\/g, '/')) {
      case '/tmp': case '/tmp/': case drive+':\\tmp': case drive+':\\tmp\\':
        ret = { isDirectory: function() { return true } }
        break
      case '/tmp/a': case drive+':/tmp/a':
        ret = { isDirectory: function() { return false } }
        break
    }
    return ret
  }

  fs.stat = function(path, cb) {
    var f = fakeStat(path);
    if (f) {
      process.nextTick(function() {
        cb(null, f)
      })
    } else {
      stat.call(fs, path, cb)
    }
  }

  fs.statSync = function(path) {
    return fakeStat(path) || statSync.call(fs, path)
  }

  function fakeReaddir(path) {
    var ret
    switch (path.toLowerCase().replace(/\\/g, '/')) {
      case '/tmp': case '/tmp/': case drive+':/tmp': case drive+':/tmp/':
        ret = [ 'a', 'A' ]
        break
      case '/': case drive+':/':
        ret = ['tmp', 'tMp', 'tMP', 'TMP']
    }
    return ret
  }

  fs.readdir = function(path, cb) {
    var f = fakeReaddir(path)
    if (f)
      process.nextTick(function() {
        cb(null, f)
      })
    else
      readdir.call(fs, path, cb)
  }

  fs.readdirSync = function(path) {
    return fakeReaddir(path) || readdirSync.call(fs, path)
  }

  t.pass('mocked')
  t.end()
})

test('nocase, nomagic', function(t) {
  var n = 2
  var want = [ '/TMP/A',
               '/TMP/a',
               '/tMP/A',
               '/tMP/a',
               '/tMp/A',
               '/tMp/a',
               '/tmp/A',
               '/tmp/a' ]
  if(process.platform.match(/^win/)) {
    want = want.map(function(p) {
      return drive+':' + p
    })
  }
  glob('/tmp/a', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/))
      res = res.map(function (r) {
        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':'), drive+':')
      })
    t.same(res.sort(), want)
    if (--n === 0) t.end()
  })
  glob('/tmp/A', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/))
      res = res.map(function (r) {
        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':'), drive+':')
      })
    t.same(res.sort(), want)
    if (--n === 0) t.end()
  })
})

test('nocase, with some magic', function(t) {
  t.plan(2)
  var want = [ '/TMP/A',
               '/TMP/a',
               '/tMP/A',
               '/tMP/a',
               '/tMp/A',
               '/tMp/a',
               '/tmp/A',
               '/tmp/a' ]
  if(process.platform.match(/^win/)) {
    want = want.map(function(p) {
      return drive + ':' + p
    })
  }

  glob('/tmp/*', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/))
      res = res.map(function (r) { return r.replace(/\\/g, '/') })
    t.same(res.sort(), want)
  })
  glob('/tmp/*', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/))
      res = res.map(function (r) { return r.replace(/\\/g, '/') })
    t.same(res.sort(), want)
  })
})
<commit_msg>test: clean up case-sensitive drive letter breakage on windows<commit_after>require("./global-leakage.js")
var fs = require('fs')
var test = require('tap').test;
var glob = require('../');

var cwd = process.cwd()
var drive = 'c'
if (/^[a-zA-Z]:[\\\/]/.test(cwd)) {
  drive = cwd.charAt(0).toLowerCase()
}

test('mock fs', function(t) {
  var stat = fs.stat
  var statSync = fs.statSync
  var readdir = fs.readdir
  var readdirSync = fs.readdirSync

  function fakeStat(path) {
    var ret
    switch (path.toLowerCase().replace(/\\/g, '/')) {
      case '/tmp': case '/tmp/': case drive+':\\tmp': case drive+':\\tmp\\':
        ret = { isDirectory: function() { return true } }
        break
      case '/tmp/a': case drive+':/tmp/a':
        ret = { isDirectory: function() { return false } }
        break
    }
    return ret
  }

  fs.stat = function(path, cb) {
    var f = fakeStat(path);
    if (f) {
      process.nextTick(function() {
        cb(null, f)
      })
    } else {
      stat.call(fs, path, cb)
    }
  }

  fs.statSync = function(path) {
    return fakeStat(path) || statSync.call(fs, path)
  }

  function fakeReaddir(path) {
    var ret
    switch (path.toLowerCase().replace(/\\/g, '/')) {
      case '/tmp': case '/tmp/': case drive+':/tmp': case drive+':/tmp/':
        ret = [ 'a', 'A' ]
        break
      case '/': case drive+':/':
        ret = ['tmp', 'tMp', 'tMP', 'TMP']
    }
    return ret
  }

  fs.readdir = function(path, cb) {
    var f = fakeReaddir(path)
    if (f)
      process.nextTick(function() {
        cb(null, f)
      })
    else
      readdir.call(fs, path, cb)
  }

  fs.readdirSync = function(path) {
    return fakeReaddir(path) || readdirSync.call(fs, path)
  }

  t.pass('mocked')
  t.end()
})

test('nocase, nomagic', function(t) {
  var n = 2
  var want = [ '/TMP/A',
               '/TMP/a',
               '/tMP/A',
               '/tMP/a',
               '/tMp/A',
               '/tMp/a',
               '/tmp/A',
               '/tmp/a' ]
  if(process.platform.match(/^win/)) {
    want = want.map(function(p) {
      return drive+':' + p
    })
  }
  glob('/tmp/a', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/))
      res = res.map(function (r) {
        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
      })
    t.same(res.sort(), want)
    if (--n === 0) t.end()
  })
  glob('/tmp/A', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/))
      res = res.map(function (r) {
        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
      })
    t.same(res.sort(), want)
    if (--n === 0) t.end()
  })
})

test('nocase, with some magic', function(t) {
  t.plan(2)
  var want = [ '/TMP/A',
               '/TMP/a',
               '/tMP/A',
               '/tMP/a',
               '/tMp/A',
               '/tMp/a',
               '/tmp/A',
               '/tmp/a' ]
  if(process.platform.match(/^win/)) {
    want = want.map(function(p) {
      return drive + ':' + p
    })
  }

  glob('/tmp/*', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/)) {
      res = res.map(function (r) {
        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
      })
    }
    t.same(res.sort(), want)
  })
  glob('/tmp/*', { nocase: true }, function(er, res) {
    if (er)
      throw er
    if (process.platform.match(/^win/)) {
      res = res.map(function (r) {
        return r.replace(/\\/g, '/').replace(new RegExp('^' + drive + ':', 'i'), drive+':')
      })
    }
    t.same(res.sort(), want)
  })
})
<|endoftext|>


<commit_before>/*jslint node: true */
/*jslint nomen: true */
/*jslint expr: true */
/*globals describe, it */

var _ = require('lodash');
var chai = require('chai');
var expect = chai.expect;

var normalize = require('..');

var AMAZON_URLS = [
  {
    raw: 'http://www.amazon.co.jp/KiRa-KiRa-Sensation-Happy-maker-μ’s/dp/B00JZ20PEA/',
    normalized: 'http://www.amazon.co.jp/dp/B00JZ20PEA',
    asin: 'B00JZ20PEA'
  },
  {
    raw: 'http://www.amazon.co.jp/gp/product/B00PL9FQBS',
    normalized: 'http://www.amazon.co.jp/dp/B00PL9FQBS',
    asin: 'B00PL9FQBS'
  },
  {
    raw: 'http://www.amazon.co.jp/dp/B00Q5Y1P5Y/ref=s9_qpp_gw_p15_d7_i3',
    normalized: 'http://www.amazon.co.jp/dp/B00Q5Y1P5Y',
    asin: 'B00Q5Y1P5Y'
  },
  {
    raw: 'http://www.amazon.co.jp/ソフトウェア・テストの技法-G-J-マイヤーズ/dp/4764900599',
    normalized: 'http://www.amazon.co.jp/dp/4764900599',
    asin: '4764900599'
  }
];

describe('Normalize test', function () {
  'use strict';

  _.each(AMAZON_URLS, function (url, index) {
    var subject = 'should normalize url (index = ' + index + ', asin = ' + url.asin + ')';

    it(subject, function (done) {
      normalize(url.raw, function (err, params) {
        try {
          expect(err).to.not.be.ok;
          expect(params.asin).to.equal(url.asin);
          expect(params.url).to.equal(url.normalized);
          done();
        }

        catch (e) {
          done(e);
        }
      });
    });
  });

  it('should normalize url with options', function (done) {
    var url = AMAZON_URLS[0];
    var options = {
      prefix: '[PREFIX]',
      suffix: '[SUFFIX]'
    };

    normalize(url.raw, options, function (err, params) {
      try {
        expect(err).to.not.be.ok;
        expect(params.asin).to.equal(url.asin);
        expect(params.url).to.equal(options.prefix + url.asin + options.suffix);
        done();
      }

      catch (e) {
        done(e);
      }
    });
  });

  it('should normalize url failed because url is invalid', function (done) {
    normalize('http://www.xamazon.co.jp/invalid_url', function (err, params) {
      try {
        expect(err).to.be.ok;
        done();
      }

      catch (e) {
        done(e);
      }
    });
  });

  it('should normalize url failed because url isn\'t a item page', function (done) {
    var url = 'http://www.amazon.co.jp/gp/help/customer/display.html?ie=UTF8&nodeId=642938';
    normalize(url, function (err, params) {
      try {
        expect(err).to.be.ok;
        done();
      }

      catch (e) {
        done(e);
      }
    });
  });
});<commit_msg>Add test<commit_after>/*jslint node: true */
/*jslint nomen: true */
/*jslint expr: true */
/*globals describe, it */

var _ = require('lodash');
var chai = require('chai');
var expect = chai.expect;

var normalize = require('..');

var AMAZON_URLS = [
  {
    raw: 'http://www.amazon.co.jp/KiRa-KiRa-Sensation-Happy-maker-μ’s/dp/B00JZ20PEA/',
    normalized: 'http://www.amazon.co.jp/dp/B00JZ20PEA',
    asin: 'B00JZ20PEA'
  },
  {
    raw: 'http://www.amazon.co.jp/gp/product/B00PL9FQBS',
    normalized: 'http://www.amazon.co.jp/dp/B00PL9FQBS',
    asin: 'B00PL9FQBS'
  },
  {
    raw: 'http://www.amazon.co.jp/dp/B00Q5Y1P5Y/ref=s9_qpp_gw_p15_d7_i3',
    normalized: 'http://www.amazon.co.jp/dp/B00Q5Y1P5Y',
    asin: 'B00Q5Y1P5Y'
  },
  {
    raw: 'http://www.amazon.co.jp/ソフトウェア・テストの技法-G-J-マイヤーズ/dp/4764900599',
    normalized: 'http://www.amazon.co.jp/dp/4764900599',
    asin: '4764900599'
  },
  {
    raw: 'http://www.amazon.co.jp/GAMBARU-SHOP-iPhone5%E5%AF%BE%E5%BF%9C-%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%84-%E3%82%A2%E3%83%BC%E3%83%A0%E3%83%90%E3%83%B3%E3%83%89/dp/B00D8QF4A4/ref=sr_1_6?ie=UTF8&qid=1425571891&sr=8-6&keywords=%E3%83%A9%E3%83%B3%E3%83%8B%E3%83%B3%E3%82%B0',
    normalized: 'http://www.amazon.co.jp/dp/B00D8QF4A4',
    asin: 'B00D8QF4A4'
  }
];

describe('Normalize test', function () {
  'use strict';

  _.each(AMAZON_URLS, function (url, index) {
    var subject = 'should normalize url (index = ' + index + ', asin = ' + url.asin + ')';

    it(subject, function (done) {
      normalize(url.raw, function (err, params) {
        try {
          expect(err).to.not.be.ok;
          expect(params.asin).to.equal(url.asin);
          expect(params.url).to.equal(url.normalized);
          done();
        }

        catch (e) {
          done(e);
        }
      });
    });
  });

  it('should normalize url with options', function (done) {
    var url = AMAZON_URLS[0];
    var options = {
      prefix: '[PREFIX]',
      suffix: '[SUFFIX]'
    };

    normalize(url.raw, options, function (err, params) {
      try {
        expect(err).to.not.be.ok;
        expect(params.asin).to.equal(url.asin);
        expect(params.url).to.equal(options.prefix + url.asin + options.suffix);
        done();
      }

      catch (e) {
        done(e);
      }
    });
  });

  it('should normalize url failed because url is invalid', function (done) {
    normalize('http://www.xamazon.co.jp/invalid_url', function (err, params) {
      try {
        expect(err).to.be.ok;
        done();
      }

      catch (e) {
        done(e);
      }
    });
  });

  it('should normalize url failed because url isn\'t a item page', function (done) {
    var url = 'http://www.amazon.co.jp/gp/help/customer/display.html?ie=UTF8&nodeId=642938';
    normalize(url, function (err, params) {
      try {
        expect(err).to.be.ok;
        done();
      }

      catch (e) {
        done(e);
      }
    });
  });
});<|endoftext|>


<commit_before>/*
Copyright (C) 2014, 2017 Kano Computing Ltd.
License: http://opensource.org/licenses/MIT The MIT License (MIT)
*/

var should = require('should'),
    util = require('./util'),
    profanity = require('../lib/profanity');

describe('Profanity module', function () {
    describe('.check(target)', function () {

        it('returns null with no swearwords found in string', function () {
            should(profanity.check('No swearwords here')).eql([]);
        });

        it('returns array of swearwords found in dirty string', function () {
            var results = profanity.check('something damn something something poo something');

            should(results).eql([
                'damn',
                'poo'
            ]);
        });

        it('doesn\'t target substrings', function () {
            var detected = profanity.check('foo ass bar'),
                notDetected = profanity.check('foo grass bar');

            should(detected).have.length(1);
            should(notDetected).have.length(0);
        });

        it('does target substrings with the substrings option', function () {
            var detected = profanity.check('foo ass bar', {substrings: true}),
                alsoDetected = profanity.check('foo grass bar', {substrings: true});

            should(detected).have.length(1);
            should(alsoDetected).have.length(1);
        });

        it('works with a custom list (the legacy way)', function () {
            var string = 'something daisy something something lol woot woot something',
                list = ['daisy', 'lol', 'woot'],
                results = profanity.check(string, list);

            should(results).eql([
                'daisy',
                'lol',
                'woot',
                'woot'
            ]);
        });

        it('works with a custom list (via options)', function () {
            var string = 'something daisy something something lol woot woot something',
                list = ['daisy', 'lol', 'woot'],
                results = profanity.check(string, {forbiddenList: list});

            should(results).eql([
                'daisy',
                'lol',
                'woot',
                'woot'
            ]);
        });

        it('works equally for objects (Recursively) and arrays', function (done) {
            var results_obj = profanity.check({
                    foo: 'something damn',
                    bar: { test: 'something poo', bar: 'crap woooh' }
                }),
                results_arr = profanity.check([
                    'something damn',
                    ['something poo'],
                    { foo: [{ bar: 'something crap' }] }
                ]);

            should(results_obj).eql([
                'damn',
                'poo',
                'crap'
            ]);

            should(results_arr).eql([
                'damn',
                'poo',
                'crap'
            ]);

            done();
        });

    });
    describe('.purify(target)', function () {
        it('works in obscure (default) mode on a simple string', function (done) {
            var result = profanity.purify('boob damn something poo');

            result[0].should.equal('b**b d**n something p*o');
            result[1].should.eql(['boob', 'damn', 'poo']);

            done();
        });

        it('works in obscure (default) mode recursively with objects', function (done) {
            var result = profanity.purify({
                bar: { foo: 'something boob', bar: { foo: 'test poo' } },
                test: 'something damn'
            });

            result[0].should.have.keys('bar', 'test');
            result[0].bar.should.have.keys('foo', 'bar');
            result[0].bar.foo.should.equal('something b**b');
            result[0].bar.bar.should.have.keys('foo');
            result[0].bar.bar.foo.should.equal('test p*o');
            result[0].bar.foo.should.equal('something b**b');
            result[0].test.should.equal('something d**n');

            result[1].should.eql(['boob', 'poo', 'damn']);

            done();
        });

        it('works in obscure (default) mode on a simple string with substrings', function (done) {
            var result = profanity.purify('boob damn something poo grass5', {substrings: true});

            result[0].should.equal('b**b d**n something p*o gra*s5');
            result[1].should.eql(['boob', 'damn', 'poo', 'ass']);

            done();
        });

        it('works in replace mode on a simple string', function (done) {
            var result = profanity.purify('Boob damn something poo', {
                replace: true
            });

            util.testPurified(result[0], '[ placeholder ] [ placeholder ] something [ placeholder ]');
            result[1].should.eql(['Boob', 'damn', 'poo']);

            done();
        });

        it('works in replace mode recursively with objects', function (done) {
            var result = profanity.purify({
                bar: { foo: 'something boob', bar: { foo: 'test poo' } },
                test: 'something damn'
            }, {
                replace: true
            });

            result[0].should.have.keys('bar', 'test');
            result[0].bar.should.have.keys('foo', 'bar');
            util.testPurified(result[0].bar.foo, 'something [ placeholder ]');
            result[0].bar.bar.should.have.keys('foo');
            util.testPurified(result[0].bar.bar.foo, 'test [ placeholder ]');
            util.testPurified(result[0].bar.foo, 'something [ placeholder ]');
            util.testPurified(result[0].test, 'something [ placeholder ]');

            result[1].should.eql(['boob', 'poo', 'damn']);

            done();
        });

        it('works in replace mode with mapping on a simple string', function (done) {
            var result = profanity.purify('boob damn something poo|boob damn something poo', {
                replace: true,
                map: true
            }),
                parts = result[0].split('|'),
                seq = '[ placeholder ] [ placeholder ] something [ placeholder ]';

            util.testPurified(result[0], seq + '|' + seq);
            parts[0].should.eql(parts[1]);
            result[1].should.eql(['boob', 'damn', 'poo', 'boob', 'damn', 'poo']);

            done();
        });

        it('works in replace mode with mapping recursively with objects', function (done) {
            var result = profanity.purify({
                foo: { foo: 'damn something boob', bar: { foo: 'test poo damn' } },
                bar: { foo: 'damn something boob', bar: { foo: 'test poo damn' } },
                test: 'something damn boob'
            }, {
                replace: true,
                map: true
            });

            result[0].should.have.keys('foo', 'bar', 'test');

            // .foo
            result[0].foo.should.have.keys('foo', 'bar');
            util.testPurified(result[0].foo.foo, '[ placeholder ] something [ placeholder ]');
            result[0].foo.bar.should.have.keys('foo');
            util.testPurified(result[0].foo.bar.foo, 'test [ placeholder ] [ placeholder ]');

            // .bar
            result[0].bar.should.have.keys('foo', 'bar');
            util.testPurified(result[0].bar.foo, '[ placeholder ] something [ placeholder ]');
            result[0].bar.bar.should.have.keys('foo');
            util.testPurified(result[0].bar.bar.foo, 'test [ placeholder ] [ placeholder ]');

            util.testPurified(result[0].test, 'something [ placeholder ] [ placeholder ]');

            result[0].foo.should.eql(result[0].bar);

            result[1].should.eql(['damn', 'boob', 'poo', 'damn',
                                  'damn', 'boob', 'poo', 'damn',
                                  'damn', 'boob']);

            done();
        });
    });
});
<commit_msg>Add test for substring lite option<commit_after>/*
Copyright (C) 2014, 2017 Kano Computing Ltd.
License: http://opensource.org/licenses/MIT The MIT License (MIT)
*/

var should = require('should'),
    util = require('./util'),
    profanity = require('../lib/profanity');

describe('Profanity module', function () {
    describe('.check(target)', function () {

        it('returns null with no swearwords found in string', function () {
            should(profanity.check('No swearwords here')).eql([]);
        });

        it('returns array of swearwords found in dirty string', function () {
            var results = profanity.check('something damn something something poo something');

            should(results).eql([
                'damn',
                'poo'
            ]);
        });

        it('doesn\'t target substrings', function () {
            var detected = profanity.check('foo ass bar'),
                notDetected = profanity.check('foo grass bar');

            should(detected).have.length(1);
            should(notDetected).have.length(0);
        });

        it('does target substrings with the substrings option', function () {
            var detected = profanity.check('foo ass bar', {substrings: true}),
                alsoDetected = profanity.check('foo grass bar', {substrings: true});

            should(detected).have.length(1);
            should(alsoDetected).have.length(1);
        });

        it('substring matching is less strict with the substrings option "lite", e.g not matching "liverpool"', function () {
            var detected = profanity.check( 'foo ass bar', { substrings: "lite" } ),
                alsoDetected = profanity.check('foo grass bar liverpool grasscutta', {substrings: true}),
                notDetected = profanity.check( 'foo grass bar liverpool grasscutta', { substrings: "lite" });

            should( detected ).have.length( 1 );
            should(alsoDetected).have.length( 3 );
            should( notDetected ).have.length( 0 );
        });

        it('works with a custom list (the legacy way)', function () {
            var string = 'something daisy something something lol woot woot something',
                list = ['daisy', 'lol', 'woot'],
                results = profanity.check(string, list);

            should(results).eql([
                'daisy',
                'lol',
                'woot',
                'woot'
            ]);
        });

        it('works with a custom list (via options)', function () {
            var string = 'something daisy something something lol woot woot something',
                list = ['daisy', 'lol', 'woot'],
                results = profanity.check(string, {forbiddenList: list});

            should(results).eql([
                'daisy',
                'lol',
                'woot',
                'woot'
            ]);
        });

        it('works equally for objects (Recursively) and arrays', function (done) {
            var results_obj = profanity.check({
                    foo: 'something damn',
                    bar: { test: 'something poo', bar: 'crap woooh' }
                }),
                results_arr = profanity.check([
                    'something damn',
                    ['something poo'],
                    { foo: [{ bar: 'something crap' }] }
                ]);

            should(results_obj).eql([
                'damn',
                'poo',
                'crap'
            ]);

            should(results_arr).eql([
                'damn',
                'poo',
                'crap'
            ]);

            done();
        });

    });
    describe('.purify(target)', function () {
        it('works in obscure (default) mode on a simple string', function (done) {
            var result = profanity.purify('boob damn something poo');

            result[0].should.equal('b**b d**n something p*o');
            result[1].should.eql(['boob', 'damn', 'poo']);

            done();
        });

        it('works in obscure (default) mode recursively with objects', function (done) {
            var result = profanity.purify({
                bar: { foo: 'something boob', bar: { foo: 'test poo' } },
                test: 'something damn'
            });

            result[0].should.have.keys('bar', 'test');
            result[0].bar.should.have.keys('foo', 'bar');
            result[0].bar.foo.should.equal('something b**b');
            result[0].bar.bar.should.have.keys('foo');
            result[0].bar.bar.foo.should.equal('test p*o');
            result[0].bar.foo.should.equal('something b**b');
            result[0].test.should.equal('something d**n');

            result[1].should.eql(['boob', 'poo', 'damn']);

            done();
        });

        it('works in obscure (default) mode on a simple string with substrings', function (done) {
            var result = profanity.purify('boob damn something poo grass5', {substrings: true});

            result[0].should.equal('b**b d**n something p*o gra*s5');
            result[1].should.eql(['boob', 'damn', 'poo', 'ass']);

            done();
        });

        it('works in replace mode on a simple string', function (done) {
            var result = profanity.purify('Boob damn something poo', {
                replace: true
            });

            util.testPurified(result[0], '[ placeholder ] [ placeholder ] something [ placeholder ]');
            result[1].should.eql(['Boob', 'damn', 'poo']);

            done();
        });

        it('works in replace mode recursively with objects', function (done) {
            var result = profanity.purify({
                bar: { foo: 'something boob', bar: { foo: 'test poo' } },
                test: 'something damn'
            }, {
                replace: true
            });

            result[0].should.have.keys('bar', 'test');
            result[0].bar.should.have.keys('foo', 'bar');
            util.testPurified(result[0].bar.foo, 'something [ placeholder ]');
            result[0].bar.bar.should.have.keys('foo');
            util.testPurified(result[0].bar.bar.foo, 'test [ placeholder ]');
            util.testPurified(result[0].bar.foo, 'something [ placeholder ]');
            util.testPurified(result[0].test, 'something [ placeholder ]');

            result[1].should.eql(['boob', 'poo', 'damn']);

            done();
        });

        it('works in replace mode with mapping on a simple string', function (done) {
            var result = profanity.purify('boob damn something poo|boob damn something poo', {
                replace: true,
                map: true
            }),
                parts = result[0].split('|'),
                seq = '[ placeholder ] [ placeholder ] something [ placeholder ]';

            util.testPurified(result[0], seq + '|' + seq);
            parts[0].should.eql(parts[1]);
            result[1].should.eql(['boob', 'damn', 'poo', 'boob', 'damn', 'poo']);

            done();
        });

        it('works in replace mode with mapping recursively with objects', function (done) {
            var result = profanity.purify({
                foo: { foo: 'damn something boob', bar: { foo: 'test poo damn' } },
                bar: { foo: 'damn something boob', bar: { foo: 'test poo damn' } },
                test: 'something damn boob'
            }, {
                replace: true,
                map: true
            });

            result[0].should.have.keys('foo', 'bar', 'test');

            // .foo
            result[0].foo.should.have.keys('foo', 'bar');
            util.testPurified(result[0].foo.foo, '[ placeholder ] something [ placeholder ]');
            result[0].foo.bar.should.have.keys('foo');
            util.testPurified(result[0].foo.bar.foo, 'test [ placeholder ] [ placeholder ]');

            // .bar
            result[0].bar.should.have.keys('foo', 'bar');
            util.testPurified(result[0].bar.foo, '[ placeholder ] something [ placeholder ]');
            result[0].bar.bar.should.have.keys('foo');
            util.testPurified(result[0].bar.bar.foo, 'test [ placeholder ] [ placeholder ]');

            util.testPurified(result[0].test, 'something [ placeholder ] [ placeholder ]');

            result[0].foo.should.eql(result[0].bar);

            result[1].should.eql(['damn', 'boob', 'poo', 'damn',
                                  'damn', 'boob', 'poo', 'damn',
                                  'damn', 'boob']);

            done();
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

// Set config so that our test server doesn't interfere with any production server. We need to set
// the config first so that all of the following code uses this config.
var config = require('../config'),
  testConfig = require('./config');
for (var i in testConfig) {
  config[i] = testConfig[i];
}

// Uncomment for debugging
// (function() {
//     var childProcess = require("child_process");
//     var oldSpawn = childProcess.spawn;
//     function mySpawn() {
//         console.log('spawn called');
//         console.log(arguments);
//         var result = oldSpawn.apply(this, arguments);
//         return result;
//     }
//     childProcess.spawn = mySpawn;
// })();

var fs = require('fs'),
  spawn = require('child_process').spawn,
  config = require('../config'),
  Partitioner = require('../scripts/partitioner/sql'),
  Manager = require('../scripts/manager'),
  System = require('../scripts/system'),
  DBMissingError = require('../scripts/client/db-missing-error'),
  log = require('../scripts/client/log');

var Server = function () {
  this._partitioner = new Partitioner();
  this._manager = new Manager(this._partitioner);
  this._system = new System(this._manager);
};

/**
 * Destroy and then create the system DB so that we have a fresh instance and Admin Party is
 * enabled
 */
Server.prototype._destroyAndCreateSystemDB = function () {
  var self = this,
    adminParty = true;
  return self._system.destroy().catch(function (err) {
    // Ignore errors caused from missing DB
    if (!(err instanceof DBMissingError)) {
      throw err;
    }
  }).then(function () {
    return self._system.create(adminParty);
  }).then(function () {
    return self._partitioner.closeDatabase(); // close DB connection to return resources
  });
};

Server.prototype._spawn = function (serverFilename) {
  this._log = fs.createWriteStream('./test/' + serverFilename, {
    flags: 'w'
  });

  this._server = spawn('./bin/deltadb-server', [
    '--port', config.PORT,
    '--prefix', config.DB_NAME_PREFIX
  ]);

  this._server.stdout.pipe(this._log);
  this._server.stderr.pipe(this._log);
};

Server.prototype._kill = function () {
  this._server.kill();
};

Server.prototype.start = function (serverFilename) {
  var self = this;
  return self._destroyAndCreateSystemDB().then(function () {
    self._spawn(serverFilename); // start test server
  });
};

Server.prototype.stop = function () {
  var self = this;
  self._server.kill(); // kill test server
  return self._system.destroy().then(function () {
    return self._partitioner.closeDatabase(); // close DB connection to return resources
  }).then(function () {
    log.stream(false); // disable client log
  });
};

module.exports = Server;
<commit_msg>test(server): remove mydb<commit_after>'use strict';

// Set config so that our test server doesn't interfere with any production server. We need to set
// the config first so that all of the following code uses this config.
var config = require('../config'),
  testConfig = require('./config');
for (var i in testConfig) {
  config[i] = testConfig[i];
}

// Uncomment for debugging
// (function() {
//     var childProcess = require("child_process");
//     var oldSpawn = childProcess.spawn;
//     function mySpawn() {
//         console.log('spawn called');
//         console.log(arguments);
//         var result = oldSpawn.apply(this, arguments);
//         return result;
//     }
//     childProcess.spawn = mySpawn;
// })();

var fs = require('fs'),
  spawn = require('child_process').spawn,
  config = require('../config'),
  Partitioner = require('../scripts/partitioner/sql'),
  Manager = require('../scripts/manager'),
  System = require('../scripts/system'),
  DBMissingError = require('../scripts/client/db-missing-error'),
  log = require('../scripts/client/log');

var Server = function () {
  this._partitioner = new Partitioner();
  this._manager = new Manager(this._partitioner);
  this._system = new System(this._manager);
};

/**
 * Destroy and then create the system DB so that we have a fresh instance and Admin Party is
 * enabled
 */
Server.prototype._destroyAndCreateSystemDB = function () {
  var self = this,
    adminParty = true;
  return self._system.destroy().catch(function (err) {
    // Ignore errors caused from missing DB
    if (!(err instanceof DBMissingError)) {
      throw err;
    }
  }).then(function () {
    // Destroy "mydb" in case a previous set of tests failed and left it behind. This saves us from
    // having to manually remove "mydb" when adding new code that breaks tests.
    return self._partitioner.destroyAnotherDatabase('mydb');
  }).catch(function (err) {
    // Ignore errors caused from "mydb" missing which is actually the expected case as long as the
    // previous set of tests passed
    if (!(err instanceof DBMissingError)) {
      throw err;
    }
  }).then(function () {
    return self._system.create(adminParty);
  }).then(function () {
    return self._partitioner.closeDatabase(); // close DB connection to return resources
  });
};

Server.prototype._spawn = function (serverFilename) {
  this._log = fs.createWriteStream('./test/' + serverFilename, {
    flags: 'w'
  });

  this._server = spawn('./bin/deltadb-server', [
    '--port', config.PORT,
    '--prefix', config.DB_NAME_PREFIX
  ]);

  this._server.stdout.pipe(this._log);
  this._server.stderr.pipe(this._log);
};

Server.prototype._kill = function () {
  this._server.kill();
};

Server.prototype.start = function (serverFilename) {
  var self = this;
  return self._destroyAndCreateSystemDB().then(function () {
    self._spawn(serverFilename); // start test server
  });
};

Server.prototype.stop = function () {
  var self = this;
  self._server.kill(); // kill test server
  return self._system.destroy().then(function () {
    return self._partitioner.closeDatabase(); // close DB connection to return resources
  }).then(function () {
    log.stream(false); // disable client log
  });
};

module.exports = Server;
<|endoftext|>


<commit_before>"use strict";

describe("luga", function(){

	it("Requires jQuery in order to work", function(){
		expect(jQuery).toBeDefined();
	});

	it("Lives inside its own namespace", function(){
		expect(luga).toBeDefined();
	});

	describe(".namespace()", function(){

		it("Does not override existing namespaces", function(){
			var testRoot = {};
			testRoot.child = {};
			testRoot.child.grandChild = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child.grandChild).toBeDefined();
		});

		it("By default uses window as root object", function(){
			luga.namespace("ciccio");
			expect(ciccio).toBeDefined();
		});

		it("But works on any arbitrary root object", function(){
			var testRoot = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child).toBeDefined();
		});

	});

	describe(".extend()", function(){

		it("Offers a simple solution for inheritance among classes", function(){

			/* Class Person. */
			function Person(gender){
				this.gender = gender;
				this.getGender = function(){
					return this.gender;
				};
			}

			/* Class Superstar */
			function Superstar(gender, name){
				luga.extend(Person, this, arguments);
				this.gender = gender;
				this.name = name;
				this.getName = function(){
					return this.name;
				};
			}

			var nicole = new Superstar("female", "Nicole Kidman");

			expect(nicole.getGender()).toEqual("female");
			expect(nicole.getName()).toEqual("Nicole Kidman")
		});

	});

	describe(".lookup(). Given the name of a function as a string", function(){

		it("Returns the relevant function if it finds it inside the window/global scope", function(){
			window.myFunc = function(){
			};
			var result = luga.lookup("myFunc");
			expect(result).not.toBeNull();
			expect(jQuery.isFunction(result)).toBeTruthy();
		});

		it("Or any namespace", function(){
			var mySpace = {};
			mySpace.myFunction = function(){
			};
			var result = luga.lookup("luga.namespace");
			expect(result).not.toBeNull();
			expect(jQuery.isFunction(result)).toBeTruthy();
		});

		it("Returns null if the function does not exist", function(){
			expect(luga.lookup("missing")).toBeNull();
		});

		it("Or if the variable exists, but it's not a function", function(){
			window.str = "ciao";
			expect(luga.lookup("str")).toBeNull();
		});

	});

	describe(".merge()", function(){

		it("Merges the contents of two objects together into the first object", function(){
			var config = {letter: "a", number: 1};
			var params = {number: 2, symbol: "@"};
			luga.merge(config, params);
			expect(config).toEqual({letter: "a", number: 2, symbol: "@"});
		});

	});

	describe(".Notifier", function(){
		var notifierObj, observerObj, uselessObj;

		beforeEach(function(){

			var NotifierClass = function(){
				luga.extend(luga.Notifier, this);
			};
			var ObserverClass = function(){
				this.completeFlag = false;
				this.onCompleteHandler = function(data){
					this.completeFlag = data.flag;
				};
				this.onSomethingHandler = function(){
				};
			};
			var UselessObserverClass = function(){
			};
			notifierObj = new NotifierClass();
			observerObj = new ObserverClass();
			uselessObj = new UselessObserverClass();
		});

		it("Is an interface class that cannot be instantiated directly, it can only be used as a base class", function(){
			expect(jQuery.isFunction(luga.Notifier)).toBeTruthy();

			expect(function(){
				new luga.Notifier();
			}).toThrow();
		});

		it("Observers are stored as an array inside the .observers property", function(){
			expect(jQuery.isArray(notifierObj.observers)).toBeTruthy();
			expect(notifierObj.observers.length).toEqual(0);
		});

		describe(".addObserver()", function(){

			it("Adds an observer object to the list of observers", function(){
				notifierObj.addObserver(observerObj);
				expect(notifierObj.observers.length).toEqual(1);
				notifierObj.addObserver(uselessObj);
				expect(notifierObj.observers.length).toEqual(2);
			});

			it("Ensures that only objects are register as observers", function(){
				expect(function(){
					notifierObj.addObserver("test");
				}).toThrow();
			});

		});

		describe(".notifyObserver()", function(){

			it("Is used to send a notification to all registered observers", function(){
				notifierObj.addObserver(observerObj);
				spyOn(observerObj, "onCompleteHandler").and.callFake(function(){
				});

				notifierObj.notifyObservers("complete", {});
				expect(observerObj.onCompleteHandler).toHaveBeenCalled();
			});

			it("Requires two parameters: eventName and data. Both are required", function(){
				expect(function(){
					notifierObj.notifyObservers("complete");
				}).toThrow();
			});

			it("The data parameter must be an object", function(){
				expect(function(){
					notifierObj.notifyObservers("complete", "ciao");
				}).toThrow();
			});

			it("Observers must follow a naming convention. For an event named 'complete' they must implement a method named: 'onCompleteHandler'", function(){
				notifierObj.addObserver(observerObj);
				spyOn(observerObj, "onCompleteHandler").and.callFake(function(){
				});
				spyOn(observerObj, "onSomethingHandler").and.callFake(function(){
				});

				notifierObj.notifyObservers("complete", {});
				expect(observerObj.onCompleteHandler).toHaveBeenCalled();
				expect(observerObj.onSomethingHandler).not.toHaveBeenCalled();
			});

			it("The data parameter provides a means for passing data from the point of notification to all interested observers", function(){
				notifierObj.addObserver(observerObj);
				expect(observerObj.completeFlag).toEqual(false);

				notifierObj.notifyObservers("complete", {flag: true});
				expect(observerObj.completeFlag).toEqual(true);
			});

		});

		describe('.removeObserver()', function(){

			it('Removes the given observer object.', function(){
				notifierObj.addObserver(observerObj);
				expect(notifierObj.observers.length).toEqual(1);
				notifierObj.removeObserver(observerObj);
				expect(notifierObj.observers.length).toEqual(0);
			});

		});

	});

});<commit_msg>Easier to understand unit tests for luga.namespace()<commit_after>"use strict";

describe("luga", function(){

	it("Requires jQuery in order to work", function(){
		expect(jQuery).toBeDefined();
	});

	it("Lives inside its own namespace", function(){
		expect(luga).toBeDefined();
	});

	describe(".namespace()", function(){

		it("Creates namespaces to be used for scoping variables and classes", function(){
			luga.namespace("myspace");
			expect(myspace).toBeDefined();
		});

		it("Does not override existing namespaces", function(){
			var testRoot = {};
			testRoot.child = {};
			testRoot.child.grandChild = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child.grandChild).toBeDefined();
		});

		it("By default uses window as root object", function(){
			luga.namespace("ciccio");
			expect(window.ciccio).toBeDefined();
		});

		it("But works on any arbitrary root object", function(){
			var testRoot = {};
			luga.namespace("child", testRoot);
			expect(testRoot.child).toBeDefined();
		});

	});

	describe(".extend()", function(){

		it("Offers a simple solution for inheritance among classes", function(){

			/* Class Person. */
			function Person(gender){
				this.gender = gender;
				this.getGender = function(){
					return this.gender;
				};
			}

			/* Class Superstar */
			function Superstar(gender, name){
				luga.extend(Person, this, arguments);
				this.gender = gender;
				this.name = name;
				this.getName = function(){
					return this.name;
				};
			}

			var nicole = new Superstar("female", "Nicole Kidman");

			expect(nicole.getGender()).toEqual("female");
			expect(nicole.getName()).toEqual("Nicole Kidman")
		});

	});

	describe(".lookup(). Given the name of a function as a string", function(){

		it("Returns the relevant function if it finds it inside the window/global scope", function(){
			window.myFunc = function(){
			};
			var result = luga.lookup("myFunc");
			expect(result).not.toBeNull();
			expect(jQuery.isFunction(result)).toBeTruthy();
		});

		it("Or any namespace", function(){
			var mySpace = {};
			mySpace.myFunction = function(){
			};
			var result = luga.lookup("luga.namespace");
			expect(result).not.toBeNull();
			expect(jQuery.isFunction(result)).toBeTruthy();
		});

		it("Returns null if the function does not exist", function(){
			expect(luga.lookup("missing")).toBeNull();
		});

		it("Or if the variable exists, but it's not a function", function(){
			window.str = "ciao";
			expect(luga.lookup("str")).toBeNull();
		});

	});

	describe(".merge()", function(){

		it("Merges the contents of two objects together into the first object", function(){
			var config = {letter: "a", number: 1};
			var params = {number: 2, symbol: "@"};
			luga.merge(config, params);
			expect(config).toEqual({letter: "a", number: 2, symbol: "@"});
		});

	});

	describe(".Notifier", function(){
		var notifierObj, observerObj, uselessObj;

		beforeEach(function(){

			var NotifierClass = function(){
				luga.extend(luga.Notifier, this);
			};
			var ObserverClass = function(){
				this.completeFlag = false;
				this.onCompleteHandler = function(data){
					this.completeFlag = data.flag;
				};
				this.onSomethingHandler = function(){
				};
			};
			var UselessObserverClass = function(){
			};
			notifierObj = new NotifierClass();
			observerObj = new ObserverClass();
			uselessObj = new UselessObserverClass();
		});

		it("Is an interface class that cannot be instantiated directly, it can only be used as a base class", function(){
			expect(jQuery.isFunction(luga.Notifier)).toBeTruthy();

			expect(function(){
				new luga.Notifier();
			}).toThrow();
		});

		it("Observers are stored as an array inside the .observers property", function(){
			expect(jQuery.isArray(notifierObj.observers)).toBeTruthy();
			expect(notifierObj.observers.length).toEqual(0);
		});

		describe(".addObserver()", function(){

			it("Adds an observer object to the list of observers", function(){
				notifierObj.addObserver(observerObj);
				expect(notifierObj.observers.length).toEqual(1);
				notifierObj.addObserver(uselessObj);
				expect(notifierObj.observers.length).toEqual(2);
			});

			it("Ensures that only objects are register as observers", function(){
				expect(function(){
					notifierObj.addObserver("test");
				}).toThrow();
			});

		});

		describe(".notifyObserver()", function(){

			it("Is used to send a notification to all registered observers", function(){
				notifierObj.addObserver(observerObj);
				spyOn(observerObj, "onCompleteHandler").and.callFake(function(){
				});

				notifierObj.notifyObservers("complete", {});
				expect(observerObj.onCompleteHandler).toHaveBeenCalled();
			});

			it("Requires two parameters: eventName and data. Both are required", function(){
				expect(function(){
					notifierObj.notifyObservers("complete");
				}).toThrow();
			});

			it("The data parameter must be an object", function(){
				expect(function(){
					notifierObj.notifyObservers("complete", "ciao");
				}).toThrow();
			});

			it("Observers must follow a naming convention. For an event named 'complete' they must implement a method named: 'onCompleteHandler'", function(){
				notifierObj.addObserver(observerObj);
				spyOn(observerObj, "onCompleteHandler").and.callFake(function(){
				});
				spyOn(observerObj, "onSomethingHandler").and.callFake(function(){
				});

				notifierObj.notifyObservers("complete", {});
				expect(observerObj.onCompleteHandler).toHaveBeenCalled();
				expect(observerObj.onSomethingHandler).not.toHaveBeenCalled();
			});

			it("The data parameter provides a means for passing data from the point of notification to all interested observers", function(){
				notifierObj.addObserver(observerObj);
				expect(observerObj.completeFlag).toEqual(false);

				notifierObj.notifyObservers("complete", {flag: true});
				expect(observerObj.completeFlag).toEqual(true);
			});

		});

		describe('.removeObserver()', function(){

			it('Removes the given observer object.', function(){
				notifierObj.addObserver(observerObj);
				expect(notifierObj.observers.length).toEqual(1);
				notifierObj.removeObserver(observerObj);
				expect(notifierObj.observers.length).toEqual(0);
			});

		});

	});

});<|endoftext|>


<commit_before>var indexJson = [];
var latestPosts;
$(document).ready(function(){

    console.log("dfdsfs");
    $.getJSON('/mkdocs/search_index.json',function(data){
        var docs = data.docs.filter(function(a){
            return a.location.indexOf('#') === -1;
        })
        indexJson = docs;
        latestPosts = $('#latestPosts');
        showDocs();
    });
});
var showDocs = function(searchstring){
    latestPosts.html('');
    var postsResultLength = 0;
    var docs = indexJson.forEach(function(a){
        if (postsResultLength < 5){
            var text = a.text;
            text = text.replace(a.title, '');
            if (text.indexOf('2015')){
                text = extractDateInfo(text, '2015');
            }else if (text.indexOf('2016')){
                text = extractDateInfo(text, '2016');
            }else if (text.indexOf('2017')){
                text = extractDateInfo(text, '2017');
            }
            latestPosts.append('
'+a.title.replace(searchstring,''+searchstring+'')+'
');
            latestPosts.append(''+text.date+'');
            latestPosts.append('
'+text.body+' ...
');
            console.log(a.text);
            postsResultLength++
        }
    });
}

var extractDateInfo = function(text, year){
    var dateText = text.substr(0, text.indexOf(year) + 4);
    var bodyText = text.substr(text.indexOf(year) + 4, 400);

    return {
        date: dateText,
        body: bodyText
    }
}<commit_msg>Removed console.log<commit_after>var indexJson = [];
var latestPosts;
$(document).ready(function(){

    $.getJSON('/mkdocs/search_index.json',function(data){
        var docs = data.docs.filter(function(a){
            return a.location.indexOf('#') === -1;
        })
        indexJson = docs;
        latestPosts = $('#latestPosts');
        showDocs();
    });
});
var showDocs = function(searchstring){
    latestPosts.html('');
    var postsResultLength = 0;
    var docs = indexJson.forEach(function(a){
        if (postsResultLength < 5){
            var text = a.text;
            text = text.replace(a.title, '');
            if (text.indexOf('2015')){
                text = extractDateInfo(text, '2015');
            }else if (text.indexOf('2016')){
                text = extractDateInfo(text, '2016');
            }else if (text.indexOf('2017')){
                text = extractDateInfo(text, '2017');
            }
            latestPosts.append('
'+a.title.replace(searchstring,''+searchstring+'')+'
');
            latestPosts.append(''+text.date+'');
            latestPosts.append('
'+text.body+' ...
');
            postsResultLength++
        }
    });
}

var extractDateInfo = function(text, year){
    var dateText = text.substr(0, text.indexOf(year) + 4);
    var bodyText = text.substr(text.indexOf(year) + 4, 400);

    return {
        date: dateText,
        body: bodyText
    }
}<|endoftext|>


<commit_before>var path = require('path');
var url = require('url');
var expect = require('chai').expect;
var pkg = path.normalize(path.join(__dirname, '..', '..', '..', 'package.json'));
var types = require('../../..').types;

describe('cli-command:', function() {
  it('should coerce single value to url', function(done) {
    var cli = require('../../..')(pkg);
    cli.configure({exit:false});
    var u = 'http://nodejs.org';
    var expected = url.parse(u, true, true);
    var args = ['-u', u];
    cli
      .option('-u, --url <url>', 'a url argument', types.url)
      .parse(args);
    expect(cli.url).to.be.an('object').that.eqls(expected);
    done();
  });
  it('should coerce scheme less url', function(done) {
    var cli = require('../../..')(pkg);
    cli.configure({exit:false});
    var u = '//nodejs.org?page=10';
    var expected = url.parse(u, true, true);
    var args = ['-u', u];
    cli
      .option('-u, --url <url>', 'a url argument', types.url)
      .parse(args);
    expect(cli.url).to.be.an('object').that.eqls(expected);
    done();
  });

  it('should error on invalid url', function(done) {
    var cli = require('../../..')(pkg);
    cli.configure({exit:false});
    var args = ['-u', '/page#about'];
    cli
      .once('error',function(e) {
        expect(cli).to.eql(this);
        //expect(code).to.eql(codes.ETYPE);
        //parameters.unshift(message);
        //console.error.apply(null, parameters);
        done();
      })
      .option('-u, --url <url>', 'a url argument', types.url)
    cli.parse(args);
  });

  it('should coerce multiple urls', function(done) {
    var cli = require('../../..')(pkg);
    cli.configure({exit:false});
    var u = '//nodejs.org#about';
    var expected = url.parse(u, true, true);
    var args = ['-u', u, '--url=' + u];
    cli
      .option('-u, --url <url...>', 'a url argument', types.url)
      .parse(args);
    expect(cli.url).to.eql([expected, expected]);
    done();
  });

})
<commit_msg>Remove migrated tests.<commit_after><|endoftext|>


<commit_before>// we've written an example test for your DOM interactive JavaScript. The html
// elements are designed to mock orange.html, however the objects and functions
// are simply suggestions. You will need to change the testing setup/assertion to
// match your code's architecture.
describe ("View", function(){
  // we're catching the globally defined variable at line 12 so our tests have
  // access to the mockGrove variable.
  var mockGrove;

  beforeEach(function() {
    // this create a fake grove and appends it to the testing DOM. Where is
    // the testing DOM? How does it relate to orange.html?
    mockGrove = document.createElement('div')
    mockGrove.className = "grove"
    document.body.appendChild(mockGrove)
  })

  afterEach(function() {
    //deletes the mockGrove object so it doesn't interfere with other tests.
    document.removeChild(mockGrove)
  })

  describe("renderTree", function() {
    var mockTemplate;

    beforeEach(function() {
      //creates a fake orange tree template div
      mockTemplate = document.createElement('div')
      mockTemplate.id = "orange-tree-template"

      // create a fake orange tree div. It's a barebones version of the tree, with
      // no p tags or buttons. You may need to add more complexity to it for future tests
      var orangeTree = document.createElement('div')
      orangeTree.className = "orange-tree"

      // appends the tree div to the template div, just like in orange.html
      mockTemplate.appendChild(orangeTree)
      // appends the template div to the document, in this case
      document.body.appendChild(mockTemplate)
    })

    afterEach(function() {
      //clean up after test.
      document.removeChild(mockTemplate)
    })

    xit("should render a tree in the grove", function(){
      var tree= new Tree()
      View.renderTree(tree)
      expect( mockGrove.childElementCount ).toEqual(1);
    });
  })

});
<commit_msg>Begin rewriting view specs<commit_after>describe ("View", function() {

    // we create a variable to store our fake 'grove' in the outer scope so that our test
    //  specs can access it
    var mockGrove;

    // we abstract out our 'grove' creation into its own function for readability purposes
    var createGrove = function() {
        mockGrove = document.createElement('div');

        mockGrove.className = "grove";

        document.body.appendChild(mockGrove);
    };

    beforeEach(function() {
        // before each test spec run, we run createGrove to generate the 'grove' DOM element
        createGrove();
    });

    afterEach(function() {
        // after each test spec run, we remove the 'grove' DOM element to create a clean slate
        //  for the next test
        document.body.removeChild(mockGrove);
    });

    describe(".renderTree()", function() {
        var mockTemplate;

        beforeEach(function() {
            //creates a fake orange tree template div
      mockTemplate = document.createElement('div')
      mockTemplate.id = "orange-tree-template"

      // create a fake orange tree div. It's a barebones version of the tree, with
      // no p tags or buttons. You may need to add more complexity to it for future tests
      var orangeTree = document.createElement('div')
      orangeTree.className = "orange-tree"

      // appends the tree div to the template div, just like in orange.html
      mockTemplate.appendChild(orangeTree)
      // appends the template div to the document, in this case
      document.body.appendChild(mockTemplate)
    })

    afterEach(function() {
      // after each test spec run, we remove the 'mockTemplate' DOM elements to create a clean slate
      //  for the next test
      document.body.removeChild(mockTemplate)
    })

    it("should render a tree in the grove", function(){
      var tree = new Tree();
      var view = new View();

      view.renderTree(tree);

      expect(mockGrove.childElementCount).toEqual(1);
    });
  })

});<|endoftext|>


<commit_before>$(function(){
	var cache = {
		cliente: null,
		general: null,
		tipos: null,
		subTipos: null,
		ramas: null,
		parametros: null,
		muestras: null,
		equiposGeneral: null,
		costoMuestra: null,
		costoParametro: null,
		peticion: null
	};

	/**
	 * Whenever an element with the class ".gridrow" is clicked, it will close the current modal
	 * and load the new url with the stored url data
	 */
	$('#modal').on('click', '.gridRow', function(){
		// Get the data and reload modal
		$('#modal').trigger('modal:reload', $(this).data('url'));
	});

	/**
	 * Whenever an element with the class "buttonSelect" is clicked, it will get the rendered model data
	 * and store in the proper cache property.
	 *
	 * After the process is done, the modal will hide
	 */
	$('#modal').on('click', '.buttonSelect', function(){
		var model = $(this).data('model');

		for(data in model)
			cache[data] = model[data];

		$('#modal').modal('hide');
		console.log(cache);
	});

	/**
	 * Whenever an element with the class "buttonSelectMultiple" is clicked, it will search the selector
	 * and store in the proper cache property.
	 *
	 * After the process is done, the modal will hide
	 */
	$('#modal').on('click', '.buttonSelectMultiple', function(){
		var selected = [];
		var selector = $('table').data('selector');

		$('.grid-view').find('input:checked').each(function(i){
			selected.push(jQuery.parseJSON($(this).val()));
		});

		cache[selector] = selected;

		$('#modal').modal('hide');
		console.log(cache);
	});

	/**
	 * Makes an Http request and renders the content in a modal if status is 200
	 * If button element has data conditions, it will process the following
	 *
	 * [data]
	 * 		title:   Sets the title of the modal
	 * 		depends: Gets the data from Cache and depending of the column to select in property will alter
	 * 				 the Http to render the desired content
	 * 		column:  Gets the value of the dependant cache property and parses it to query string in Http request
	 * 				 this is mandatory for the "depends" data to work
	 * 		array:   Sets whenever the column value is an array, so the Http request can handle the value as array
	 */
	$('.modalSelect').click(function(){
		var data   = $(this).data();
		var titulo = data.title;
		var url    = $(this).attr('value');

		if(data.depends)
		{
			var query = {};

			if(data.array){
				var summary = [];

				for(e in cache[data.depends]){
					summary.push(data.column + '[]=' + cache[data.depends][e][data.column]);
				}

				query = summary.join('&');
			}

			else
				query[data.column] = cache[data.depends][data.column];

			url = mergeParam(url, query);
		}

		$('.modal-title').text(titulo);
		$(document).trigger('modal:load', url);
	});

	/**
	 * Creates a new url with the provided url and the params to parse
	 * The params are used to parse as query string
	 *
	 * @param  {string}        url    The url to merge with the params
	 * @param  {string/object} params The element used to parse as query string
	 * @return {string}        		  The new url string
	 */
	function mergeParam (url, params){
		if(typeof params === 'string')
			return url + '?' + params;

		return url + '?' + jQuery.param(params);
	}
});<commit_msg>No se, supongo que esto es porque por la asincronia de las versiones...<commit_after>$(function(){
	var cache = {
		cliente: null,
		general: null,
		tipos: null,
		subTipos: null,
		ramas: null,
		parametros: null,
		muestras: null,
		equiposGeneral: null,
		costoMuestra: null,
		costoParametro: null,
		peticion: null
	};

	/**
	 * Whenever an element with the class ".gridrow" is clicked, it will close the current modal
	 * and load the new url with the stored url data
	 */
	$('#modal').on('click', '.gridRow', function(){
		// Get the data and reload modal
		$('#modal').trigger('modal:reload', $(this).data('url'));
	});

	/**
	 * Whenever an element with the class "buttonSelect" is clicked, it will get the rendered model data
	 * and store in the proper cache property.
	 *
	 * After the process is done, the modal will hide
	 */
	$('#modal').on('click', '.buttonSelect', function(){
		var model = $(this).data('model');

		for(data in model)
			cache[data] = model[data];

		$('#modal').modal('hide');
		console.log(cache);
	});

	/**
	 * Whenever an element with the class "buttonSelectMultiple" is clicked, it will search the selector
	 * and store in the proper cache property.
	 *
	 * After the process is done, the modal will hide
	 */
	$('#modal').on('click', '.buttonSelectMultiple', function(){
		var selected = [];
		var selector = $('table').data('selector');

		$('.grid-view').find('input:checked').each(function(i){
			selected.push(jQuery.parseJSON($(this).val()));
		});

		cache[selector] = selected;

		$('#modal').modal('hide');
		console.log(cache);
	});

	/**
	 * Makes an Http request and renders the content in a modal if status is 200
	 * If button element has data conditions, it will process the following
	 *
	 * [data]
	 * 		title:   Sets the title of the modal
	 * 		depends: Gets the data from Cache and depending of the column to select in property will alter
	 * 				 the Http to render the desired content
	 * 		column:  Gets the value of the dependant cache property and parses it to query string in Http request
	 * 				 this is mandatory for the "depends" data to work
	 * 		array:   Sets whenever the column value is an array, so the Http request can handle the value as array
	 */
	$('.modalButton').click(function(){
		var data   = $(this).data();
		var titulo = data.title;
		var url    = $(this).attr('value');

		if(data.depends)
		{
			var query = {};

			if(data.array){
				var summary = [];

				for(e in cache[data.depends]){
					summary.push(data.column + '[]=' + cache[data.depends][e][data.column]);
				}

				query = summary.join('&');
			}

			else
				query[data.column] = cache[data.depends][data.column];

			url = mergeParam(url, query);
		}

		$('.modal-title').text(titulo);
		$(document).trigger('modal:load', url);
	});

	/**
	 * Creates a new url with the provided url and the params to parse
	 * The params are used to parse as query string
	 *
	 * @param  {string}        url    The url to merge with the params
	 * @param  {string/object} params The element used to parse as query string
	 * @return {string}        		  The new url string
	 */
	function mergeParam (url, params){
		if(typeof params === 'string')
			return url + '?' + params;

		return url + '?' + jQuery.param(params);
	}
});<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var $$ = require('../../lib/js-yaml/common');

var _common = module.exports = {};
$$.extend(_common, $$);

function kindOf(object) {
  return /\[object (.+?)\]/.exec(Object.prototype.toString.call(object))[1];
}

function areEqual(value1, value2) {
  var kind1 = kindOf(value1),
      kind2 = kindOf(value2);

  if (kind1 != kind2) {
    return false;
  } else if (Number.isNaN(value1) && Number.isNaN(value2)) {
    return true;
  } else {
    switch (kind1) {
      case 'Object': return areEqualObjects(value1, value2);
      case 'Array':  return areEqualArrays(value1, value2);
      case 'Date':   return value1.getTime() === value2.getTime();
      default:       return value1 === value2;
    }
  }
}

function areEqualArrays(array1, array2) {
  var index, length;

  if (array1.length !== array2.length) {
    return false;
  }

  for (index = 0, length = array1.length; index < length; index += 1) {
    if (!areEqual(array1[index], array2[index])) {
      return false;
    }
  }

  return true;
}

function areEqualObjects(object1, object2) {
  var keys1, keys2, index, length, currentKey;

  if (Object.getPrototypeOf(object1) !== Object.getPrototypeOf(object2)) {
    return false;
  }

  keys1 = Object.keys(object1);
  keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  keys1.sort();
  keys2.sort();

  length = keys1.length;

  for (index = 0; index < length; index += 1) {
    if (keys1[index] !== keys2[index]) {
      return false;
    }
  }

  for (index = 0; index < length; index += 1) {
    currentKey = keys1[index];

    if (!areEqual(object1[currentKey], object2[currentKey])) {
      return false;
    }
  }

  return true;
}

function makeClassConstructor(Class, params) {
  var mapKeys      = params.map      || {},
      requiredKeys = params.required || [],
      optionalKeys = params.optional || [];

  return function fromYAMLNode(node) {
    var mapping = this.constructMapping(node, true);

    assert.equal(typeof mapping, 'object');

    $$.each(mapKeys, function (newKey, oldKey) {
      if (Object.prototype.hasOwnProperty.call(mapping, oldKey)) {
        mapping[newKey] = mapping[oldKey];
        delete mapping[oldKey];
      }
    });

    requiredKeys.forEach(function (key) {
      assert(Object.prototype.hasOwnProperty.call(mapping, key));
    });

    $$.each(mapping, function (value, key) {
      var hasAsRequired = (0 <= requiredKeys.indexOf(key)),
          hasAsOptional = (0 <= optionalKeys.indexOf(key));

      assert(hasAsRequired || hasAsOptional);
    });

    return new Class(mapping);
  };
}

_common.kindOf = kindOf;
_common.areEqual = areEqual;
_common.areEqualArrays = areEqualArrays;
_common.areEqualObjects = areEqualObjects;
_common.makeClassConstructor = makeClassConstructor;
<commit_msg>Remove unused exports from test/support/common<commit_after>'use strict';

var assert = require('assert');
var $$ = require('../../lib/js-yaml/common');

var _common = module.exports = {};
$$.extend(_common, $$);

function kindOf(object) {
  return /\[object (.+?)\]/.exec(Object.prototype.toString.call(object))[1];
}

function areEqual(value1, value2) {
  var kind1 = kindOf(value1),
      kind2 = kindOf(value2);

  if (kind1 != kind2) {
    return false;
  } else if (Number.isNaN(value1) && Number.isNaN(value2)) {
    return true;
  } else {
    switch (kind1) {
      case 'Object': return areEqualObjects(value1, value2);
      case 'Array':  return areEqualArrays(value1, value2);
      case 'Date':   return value1.getTime() === value2.getTime();
      default:       return value1 === value2;
    }
  }
}

function areEqualArrays(array1, array2) {
  var index, length;

  if (array1.length !== array2.length) {
    return false;
  }

  for (index = 0, length = array1.length; index < length; index += 1) {
    if (!areEqual(array1[index], array2[index])) {
      return false;
    }
  }

  return true;
}

function areEqualObjects(object1, object2) {
  var keys1, keys2, index, length, currentKey;

  if (Object.getPrototypeOf(object1) !== Object.getPrototypeOf(object2)) {
    return false;
  }

  keys1 = Object.keys(object1);
  keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  keys1.sort();
  keys2.sort();

  length = keys1.length;

  for (index = 0; index < length; index += 1) {
    if (keys1[index] !== keys2[index]) {
      return false;
    }
  }

  for (index = 0; index < length; index += 1) {
    currentKey = keys1[index];

    if (!areEqual(object1[currentKey], object2[currentKey])) {
      return false;
    }
  }

  return true;
}

function makeClassConstructor(Class, params) {
  var mapKeys      = params.map      || {},
      requiredKeys = params.required || [],
      optionalKeys = params.optional || [];

  return function fromYAMLNode(node) {
    var mapping = this.constructMapping(node, true);

    assert.equal(typeof mapping, 'object');

    $$.each(mapKeys, function (newKey, oldKey) {
      if (Object.prototype.hasOwnProperty.call(mapping, oldKey)) {
        mapping[newKey] = mapping[oldKey];
        delete mapping[oldKey];
      }
    });

    requiredKeys.forEach(function (key) {
      assert(Object.prototype.hasOwnProperty.call(mapping, key));
    });

    $$.each(mapping, function (value, key) {
      var hasAsRequired = (0 <= requiredKeys.indexOf(key)),
          hasAsOptional = (0 <= optionalKeys.indexOf(key));

      assert(hasAsRequired || hasAsOptional);
    });

    return new Class(mapping);
  };
}

_common.kindOf = kindOf;
_common.areEqual = areEqual;
_common.makeClassConstructor = makeClassConstructor;
<|endoftext|>